[dotnet] Fix Resharper code issues (#7178)

* Fix resharper code issues for language usage opportunities

* Fix resharper code issues for common practices and code improvements

* Fix resharper code issues for potential code quality issues

* Fix resharper code issues for redundancies in code

* Fix xunit test output

* Update changelog

* Fix resharper code issues for compiler warnings

* Fix resharper code issues for inconsistent naming

* Add resharper solution settings file

* Fix resharper code issues for potential code quality issues

* Fix resharper code issues for redundancies in code

* Fix resharper code issues for redundancies in symbol declarations
This commit is contained in:
Sean Fausett 2021-06-11 02:32:33 +12:00 committed by GitHub
parent 3e170f6f80
commit 3530ba3205
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
110 changed files with 624 additions and 687 deletions

View file

@ -1,5 +1,7 @@
### Improvements ### Improvements
- [dotnet] Fix Resharper code issues.
[#7178](https://github.com/pulumi/pulumi/pull/7178)
- [codegen] - Include properties with an underlying type of string on Go provider instances. - [codegen] - Include properties with an underlying type of string on Go provider instances.

View file

@ -80,8 +80,9 @@ namespace Pulumi.Automation.Tests
var program = PulumiFn.Create(() => var program = PulumiFn.Create(() =>
{ {
hitSemaphore = true; hitSemaphore = true;
// ReSharper disable once AccessToDisposedClosure
semaphore.Wait(); semaphore.Wait();
return new Dictionary<string, object?>() return new Dictionary<string, object?>
{ {
["test"] = "doesnt matter", ["test"] = "doesnt matter",
}; };

View file

@ -1,9 +1,9 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System;
using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.IO;
using System;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Xunit; using Xunit;
@ -37,12 +37,7 @@ namespace Pulumi.Automation.Tests
[Fact] [Fact]
public async Task PropagatesUserExceptionsToCaller() public async Task PropagatesUserExceptionsToCaller()
{ {
using var fx = new Fixture(); using var fx = new Fixture { Action = ev => throw new MyException() };
fx.Action = ev =>
{
throw new MyException();
};
await fx.Write("{}"); await fx.Write("{}");
@ -62,7 +57,7 @@ namespace Pulumi.Automation.Tests
private class MyException : Exception { } private class MyException : Exception { }
private class Fixture : IDisposable private sealed class Fixture : IDisposable
{ {
public int EventCounter; public int EventCounter;
public string LogFileName { get; } public string LogFileName { get; }

View file

@ -2,10 +2,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Xunit;
using Pulumi.Automation.Commands;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Pulumi.Automation.Commands;
using Xunit;
namespace Pulumi.Automation.Tests namespace Pulumi.Automation.Tests
{ {
@ -15,8 +15,8 @@ namespace Pulumi.Automation.Tests
public async Task CheckVersionCommand() public async Task CheckVersionCommand()
{ {
var localCmd = new LocalPulumiCmd(); var localCmd = new LocalPulumiCmd();
IDictionary<string, string?> extraEnv = new Dictionary<string, string?>(); var extraEnv = new Dictionary<string, string?>();
IEnumerable<string> args = new string[]{ "version" }; var args = new[] { "version" };
var stdoutLines = new List<string>(); var stdoutLines = new List<string>();
var stderrLines = new List<string>(); var stderrLines = new List<string>();

View file

@ -1,19 +1,20 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Semver;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Pulumi.Automation.Exceptions; using Microsoft.Extensions.DependencyInjection;
using Pulumi.Automation.Commands.Exceptions; using Pulumi.Automation.Commands.Exceptions;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Exceptions;
using Semver;
using Xunit; using Xunit;
using System.Collections.Immutable;
using Microsoft.Extensions.DependencyInjection;
namespace Pulumi.Automation.Tests namespace Pulumi.Automation.Tests
{ {
@ -124,7 +125,7 @@ namespace Pulumi.Automation.Tests
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{ {
ProjectSettings = projectSettings, ProjectSettings = projectSettings,
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -144,7 +145,7 @@ namespace Pulumi.Automation.Tests
stacks = await workspace.ListStacksAsync(); stacks = await workspace.ListStacksAsync();
var newStack = stacks.FirstOrDefault(s => s.Name == stackName); var newStack = stacks.FirstOrDefault(s => s.Name == stackName);
Assert.NotNull(newStack); Assert.NotNull(newStack);
Assert.True(newStack.IsCurrent); Assert.True(newStack!.IsCurrent);
await workspace.SelectStackAsync(stackName); await workspace.SelectStackAsync(stackName);
await workspace.RemoveStackAsync(stackName); await workspace.RemoveStackAsync(stackName);
@ -166,8 +167,6 @@ namespace Pulumi.Automation.Tests
ProjectSettings = projectSettings ProjectSettings = projectSettings
}); });
StackDeployment deployment;
var stackName = $"{RandomStackName()}"; var stackName = $"{RandomStackName()}";
try try
{ {
@ -178,7 +177,7 @@ namespace Pulumi.Automation.Tests
Assert.Equal(UpdateState.Succeeded, upResult.Summary.Result); Assert.Equal(UpdateState.Succeeded, upResult.Summary.Result);
Assert.Equal(3, upResult.Outputs.Count); Assert.Equal(3, upResult.Outputs.Count);
deployment = await workspace.ExportStackAsync(stackName); var deployment = await workspace.ExportStackAsync(stackName);
Assert.True(deployment.Version > 0); Assert.True(deployment.Version > 0);
var previewBeforeDestroy = await stack.PreviewAsync(); var previewBeforeDestroy = await stack.PreviewAsync();
@ -214,7 +213,7 @@ namespace Pulumi.Automation.Tests
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{ {
ProjectSettings = projectSettings, ProjectSettings = projectSettings,
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -223,12 +222,6 @@ namespace Pulumi.Automation.Tests
var stackName = $"{RandomStackName()}"; var stackName = $"{RandomStackName()}";
var stack = await WorkspaceStack.CreateAsync(stackName, workspace); var stack = await WorkspaceStack.CreateAsync(stackName, workspace);
var config = new Dictionary<string, ConfigValue>()
{
["plain"] = new ConfigValue("abc"),
["secret"] = new ConfigValue("def", isSecret: true),
};
var plainKey = NormalizeConfigKey("plain", projectName); var plainKey = NormalizeConfigKey("plain", projectName);
var secretKey = NormalizeConfigKey("secret", projectName); var secretKey = NormalizeConfigKey("secret", projectName);
@ -240,7 +233,13 @@ namespace Pulumi.Automation.Tests
var values = await stack.GetAllConfigAsync(); var values = await stack.GetAllConfigAsync();
Assert.Empty(values); Assert.Empty(values);
var config = new Dictionary<string, ConfigValue>
{
["plain"] = new ConfigValue("abc"),
["secret"] = new ConfigValue("def", isSecret: true),
};
await stack.SetAllConfigAsync(config); await stack.SetAllConfigAsync(config);
values = await stack.GetAllConfigAsync(); values = await stack.GetAllConfigAsync();
Assert.True(values.TryGetValue(plainKey, out var plainValue)); Assert.True(values.TryGetValue(plainKey, out var plainValue));
Assert.Equal("abc", plainValue!.Value); Assert.Equal("abc", plainValue!.Value);
@ -282,7 +281,7 @@ namespace Pulumi.Automation.Tests
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{ {
ProjectSettings = projectSettings, ProjectSettings = projectSettings,
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -320,7 +319,7 @@ namespace Pulumi.Automation.Tests
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{ {
ProjectSettings = projectSettings, ProjectSettings = projectSettings,
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -348,19 +347,19 @@ namespace Pulumi.Automation.Tests
var workingDir = ResourcePath(Path.Combine("Data", "testproj")); var workingDir = ResourcePath(Path.Combine("Data", "testproj"));
using var stack = await LocalWorkspace.CreateStackAsync(new LocalProgramArgs(stackName, workingDir) using var stack = await LocalWorkspace.CreateStackAsync(new LocalProgramArgs(stackName, workingDir)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
}); });
var config = new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
try try
{ {
var config = new Dictionary<string, ConfigValue>
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
await stack.SetAllConfigAsync(config); await stack.SetAllConfigAsync(config);
// pulumi up // pulumi up
@ -410,7 +409,7 @@ namespace Pulumi.Automation.Tests
{ {
var program = PulumiFn.Create(() => var program = PulumiFn.Create(() =>
{ {
var config = new Pulumi.Config(); var config = new Config();
return new Dictionary<string, object?> return new Dictionary<string, object?>
{ {
["exp_static"] = "foo", ["exp_static"] = "foo",
@ -424,19 +423,19 @@ namespace Pulumi.Automation.Tests
var projectName = "inline_node"; var projectName = "inline_node";
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
}); });
var config = new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
try try
{ {
var config = new Dictionary<string, ConfigValue>
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
await stack.SetAllConfigAsync(config); await stack.SetAllConfigAsync(config);
// pulumi up // pulumi up
@ -499,20 +498,19 @@ namespace Pulumi.Automation.Tests
var projectName = "inline_node"; var projectName = "inline_node";
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
}); });
var config = new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
try try
{ {
var config = new Dictionary<string, ConfigValue>
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
await stack.SetAllConfigAsync(config); await stack.SetAllConfigAsync(config);
var initialOutputs = await stack.GetOutputsAsync(); var initialOutputs = await stack.GetOutputsAsync();
@ -565,12 +563,10 @@ namespace Pulumi.Automation.Tests
public async Task StackReferenceDestroyDiscardsWithTwoInlinePrograms() public async Task StackReferenceDestroyDiscardsWithTwoInlinePrograms()
{ {
var programA = PulumiFn.Create(() => var programA = PulumiFn.Create(() =>
{ new Dictionary<string, object?>
return new Dictionary<string, object?>
{ {
["exp_static"] = "foo", ["exp_static"] = "foo",
}; });
});
var programB = PulumiFn.Create(() => var programB = PulumiFn.Create(() =>
{ {
@ -588,7 +584,7 @@ namespace Pulumi.Automation.Tests
var stackA = await SetupStack(projectName, stackNameA, programA, new Dictionary<string, ConfigValue>()); var stackA = await SetupStack(projectName, stackNameA, programA, new Dictionary<string, ConfigValue>());
var stackB = await SetupStack(projectName, stackNameB, programB, new Dictionary<string, ConfigValue>() var stackB = await SetupStack(projectName, stackNameB, programB, new Dictionary<string, ConfigValue>
{ {
["Ref"] = new ConfigValue(FullyQualifiedStackName(_pulumiOrg, projectName, stackNameA)), ["Ref"] = new ConfigValue(FullyQualifiedStackName(_pulumiOrg, projectName, stackNameA)),
}); });
@ -645,7 +641,7 @@ namespace Pulumi.Automation.Tests
{ {
var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(project, stackName, program) var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(project, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -661,18 +657,16 @@ namespace Pulumi.Automation.Tests
public async Task OutputStreamAndDelegateIsWritten() public async Task OutputStreamAndDelegateIsWritten()
{ {
var program = PulumiFn.Create(() => var program = PulumiFn.Create(() =>
{ new Dictionary<string, object?>
return new Dictionary<string, object?>
{ {
["test"] = "test", ["test"] = "test",
}; });
});
var stackName = $"{RandomStackName()}"; var stackName = $"{RandomStackName()}";
var projectName = "inline_output"; var projectName = "inline_output";
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -680,29 +674,27 @@ namespace Pulumi.Automation.Tests
try try
{ {
var outputCalled = false;
// pulumi preview // pulumi preview
outputCalled = false; var outputCalled = false;
var previewResult = await stack.PreviewAsync(new PreviewOptions { OnStandardOutput = (str) => outputCalled = true }); var previewResult = await stack.PreviewAsync(new PreviewOptions { OnStandardOutput = str => outputCalled = true });
Assert.False(string.IsNullOrEmpty(previewResult.StandardOutput)); Assert.False(string.IsNullOrEmpty(previewResult.StandardOutput));
Assert.True(outputCalled); Assert.True(outputCalled);
// pulumi up // pulumi up
outputCalled = false; outputCalled = false;
var upResult = await stack.UpAsync(new UpOptions { OnStandardOutput = (str) => outputCalled = true }); var upResult = await stack.UpAsync(new UpOptions { OnStandardOutput = str => outputCalled = true });
Assert.False(string.IsNullOrEmpty(upResult.StandardOutput)); Assert.False(string.IsNullOrEmpty(upResult.StandardOutput));
Assert.True(outputCalled); Assert.True(outputCalled);
// pulumi refresh // pulumi refresh
outputCalled = false; outputCalled = false;
var refreshResult = await stack.RefreshAsync(new RefreshOptions { OnStandardOutput = (str) => outputCalled = true }); var refreshResult = await stack.RefreshAsync(new RefreshOptions { OnStandardOutput = str => outputCalled = true });
Assert.False(string.IsNullOrEmpty(refreshResult.StandardOutput)); Assert.False(string.IsNullOrEmpty(refreshResult.StandardOutput));
Assert.True(outputCalled); Assert.True(outputCalled);
// pulumi destroy // pulumi destroy
outputCalled = false; outputCalled = false;
var destroyResult = await stack.DestroyAsync(new DestroyOptions { OnStandardOutput = (str) => outputCalled = true }); var destroyResult = await stack.DestroyAsync(new DestroyOptions { OnStandardOutput = str => outputCalled = true });
Assert.False(string.IsNullOrEmpty(destroyResult.StandardOutput)); Assert.False(string.IsNullOrEmpty(destroyResult.StandardOutput));
Assert.True(outputCalled); Assert.True(outputCalled);
} }
@ -716,17 +708,15 @@ namespace Pulumi.Automation.Tests
public async Task HandlesEvents() public async Task HandlesEvents()
{ {
var program = PulumiFn.Create(() => var program = PulumiFn.Create(() =>
{ new Dictionary<string, object?>
return new Dictionary<string, object?>
{ {
["exp_static"] = "foo", ["exp_static"] = "foo",
}; });
});
var projectName = "event_test"; var projectName = "event_test";
var stackName = $"inline_events{GetTestSuffix()}"; var stackName = $"inline_events{GetTestSuffix()}";
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -769,7 +759,7 @@ namespace Pulumi.Automation.Tests
{ {
var events = new List<EngineEvent>(); var events = new List<EngineEvent>();
var result = await func(new TOptions() { OnEvent = events.Add }, CancellationToken.None); var result = await func(new TOptions { OnEvent = events.Add }, CancellationToken.None);
var seenSummaryEvent = events.Any(@event => @event.SummaryEvent != null); var seenSummaryEvent = events.Any(@event => @event.SummaryEvent != null);
var seenCancelEvent = events.Any(@event => @event.CancelEvent != null); var seenCancelEvent = events.Any(@event => @event.CancelEvent != null);
@ -806,10 +796,10 @@ namespace Pulumi.Automation.Tests
config.GetSecretInt32("plainint3"); config.GetSecretInt32("plainint3");
config.RequireSecretInt32("plainint4"); config.RequireSecretInt32("plainint4");
config.GetObject<System.Text.Json.JsonElement>("plainobj1"); config.GetObject<JsonElement>("plainobj1");
config.RequireObject<System.Text.Json.JsonElement>("plainobj2"); config.RequireObject<JsonElement>("plainobj2");
config.GetSecretObject<System.Text.Json.JsonElement>("plainobj3"); config.GetSecretObject<JsonElement>("plainobj3");
config.RequireSecretObject<System.Text.Json.JsonElement>("plainobj4"); config.RequireSecretObject<JsonElement>("plainobj4");
config.Get("str1"); config.Get("str1");
config.Require("str2"); config.Require("str2");
@ -826,59 +816,59 @@ namespace Pulumi.Automation.Tests
config.GetSecretInt32("int3"); config.GetSecretInt32("int3");
config.RequireSecretInt32("int4"); config.RequireSecretInt32("int4");
config.GetObject<System.Text.Json.JsonElement>("obj1"); config.GetObject<JsonElement>("obj1");
config.RequireObject<System.Text.Json.JsonElement>("obj2"); config.RequireObject<JsonElement>("obj2");
config.GetSecretObject<System.Text.Json.JsonElement>("obj3"); config.GetSecretObject<JsonElement>("obj3");
config.RequireSecretObject<System.Text.Json.JsonElement>("obj4"); config.RequireSecretObject<JsonElement>("obj4");
}); });
var projectName = "inline_dotnet"; var projectName = "inline_dotnet";
var stackName = $"inline_dotnet{GetTestSuffix()}"; var stackName = $"inline_dotnet{GetTestSuffix()}";
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
}); });
var config = new Dictionary<string, ConfigValue>()
{
{ "plainstr1", new ConfigValue("1") },
{ "plainstr2", new ConfigValue("2") },
{ "plainstr3", new ConfigValue("3") },
{ "plainstr4", new ConfigValue("4") },
{ "plainbool1", new ConfigValue("true") },
{ "plainbool2", new ConfigValue("true") },
{ "plainbool3", new ConfigValue("true") },
{ "plainbool4", new ConfigValue("true") },
{ "plainint1", new ConfigValue("1") },
{ "plainint2", new ConfigValue("2") },
{ "plainint3", new ConfigValue("3") },
{ "plainint4", new ConfigValue("4") },
{ "plainobj1", new ConfigValue("{}") },
{ "plainobj2", new ConfigValue("{}") },
{ "plainobj3", new ConfigValue("{}") },
{ "plainobj4", new ConfigValue("{}") },
{ "str1", new ConfigValue("1", isSecret: true) },
{ "str2", new ConfigValue("2", isSecret: true) },
{ "str3", new ConfigValue("3", isSecret: true) },
{ "str4", new ConfigValue("4", isSecret: true) },
{ "bool1", new ConfigValue("true", isSecret: true) },
{ "bool2", new ConfigValue("true", isSecret: true) },
{ "bool3", new ConfigValue("true", isSecret: true) },
{ "bool4", new ConfigValue("true", isSecret: true) },
{ "int1", new ConfigValue("1", isSecret: true) },
{ "int2", new ConfigValue("2", isSecret: true) },
{ "int3", new ConfigValue("3", isSecret: true) },
{ "int4", new ConfigValue("4", isSecret: true) },
{ "obj1", new ConfigValue("{}", isSecret: true) },
{ "obj2", new ConfigValue("{}", isSecret: true) },
{ "obj3", new ConfigValue("{}", isSecret: true) },
{ "obj4", new ConfigValue("{}", isSecret: true) },
};
try try
{ {
var config = new Dictionary<string, ConfigValue>
{
{ "plainstr1", new ConfigValue("1") },
{ "plainstr2", new ConfigValue("2") },
{ "plainstr3", new ConfigValue("3") },
{ "plainstr4", new ConfigValue("4") },
{ "plainbool1", new ConfigValue("true") },
{ "plainbool2", new ConfigValue("true") },
{ "plainbool3", new ConfigValue("true") },
{ "plainbool4", new ConfigValue("true") },
{ "plainint1", new ConfigValue("1") },
{ "plainint2", new ConfigValue("2") },
{ "plainint3", new ConfigValue("3") },
{ "plainint4", new ConfigValue("4") },
{ "plainobj1", new ConfigValue("{}") },
{ "plainobj2", new ConfigValue("{}") },
{ "plainobj3", new ConfigValue("{}") },
{ "plainobj4", new ConfigValue("{}") },
{ "str1", new ConfigValue("1", isSecret: true) },
{ "str2", new ConfigValue("2", isSecret: true) },
{ "str3", new ConfigValue("3", isSecret: true) },
{ "str4", new ConfigValue("4", isSecret: true) },
{ "bool1", new ConfigValue("true", isSecret: true) },
{ "bool2", new ConfigValue("true", isSecret: true) },
{ "bool3", new ConfigValue("true", isSecret: true) },
{ "bool4", new ConfigValue("true", isSecret: true) },
{ "int1", new ConfigValue("1", isSecret: true) },
{ "int2", new ConfigValue("2", isSecret: true) },
{ "int3", new ConfigValue("3", isSecret: true) },
{ "int4", new ConfigValue("4", isSecret: true) },
{ "obj1", new ConfigValue("{}", isSecret: true) },
{ "obj2", new ConfigValue("{}", isSecret: true) },
{ "obj3", new ConfigValue("{}", isSecret: true) },
{ "obj4", new ConfigValue("{}", isSecret: true) },
};
await stack.SetAllConfigAsync(config); await stack.SetAllConfigAsync(config);
// pulumi preview // pulumi preview
@ -895,7 +885,7 @@ namespace Pulumi.Automation.Tests
static async Task<T> RunCommand<T, TOptions>(Func<TOptions, CancellationToken, Task<T>> func, string command) static async Task<T> RunCommand<T, TOptions>(Func<TOptions, CancellationToken, Task<T>> func, string command)
where TOptions : UpdateOptions, new() where TOptions : UpdateOptions, new()
{ {
var expectedWarnings = new string[] var expectedWarnings = new[]
{ {
"Configuration 'inline_dotnet:str1' value is a secret; use `GetSecret` instead of `Get`", "Configuration 'inline_dotnet:str1' value is a secret; use `GetSecret` instead of `Get`",
"Configuration 'inline_dotnet:str2' value is a secret; use `RequireSecret` instead of `Require`", "Configuration 'inline_dotnet:str2' value is a secret; use `RequireSecret` instead of `Require`",
@ -908,7 +898,7 @@ namespace Pulumi.Automation.Tests
}; };
// These keys should not be in any warning messages. // These keys should not be in any warning messages.
var unexpectedWarnings = new string[] var unexpectedWarnings = new[]
{ {
"plainstr1", "plainstr1",
"plainstr2", "plainstr2",
@ -938,7 +928,7 @@ namespace Pulumi.Automation.Tests
var events = new List<DiagnosticEvent>(); var events = new List<DiagnosticEvent>();
var result = await func(new TOptions() var result = await func(new TOptions
{ {
OnEvent = @event => OnEvent = @event =>
{ {
@ -976,7 +966,7 @@ namespace Pulumi.Automation.Tests
public ValidStack() public ValidStack()
{ {
var config = new Pulumi.Config(); var config = new Config();
this.ExpStatic = Output.Create("foo"); this.ExpStatic = Output.Create("foo");
this.ExpConfig = Output.Create(config.Get("bar")!); this.ExpConfig = Output.Create(config.Get("bar")!);
this.ExpSecret = config.GetSecret("buzz")!; this.ExpSecret = config.GetSecret("buzz")!;
@ -993,19 +983,19 @@ namespace Pulumi.Automation.Tests
var projectName = "inline_tstack_node"; var projectName = "inline_tstack_node";
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
}); });
var config = new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
try try
{ {
var config = new Dictionary<string, ConfigValue>
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
await stack.SetAllConfigAsync(config); await stack.SetAllConfigAsync(config);
// pulumi up // pulumi up
@ -1053,7 +1043,7 @@ namespace Pulumi.Automation.Tests
[Fact] [Fact]
public async Task StackLifecycleInlineProgramWithServiceProvider() public async Task StackLifecycleInlineProgramWithServiceProvider()
{ {
using var provider = new ServiceCollection() await using var provider = new ServiceCollection()
.AddTransient<ValidStack>() // must be transient so it is instantiated each time .AddTransient<ValidStack>() // must be transient so it is instantiated each time
.BuildServiceProvider(); .BuildServiceProvider();
@ -1064,19 +1054,19 @@ namespace Pulumi.Automation.Tests
var projectName = "inline_serviceprovider_node"; var projectName = "inline_serviceprovider_node";
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
}); });
var config = new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
try try
{ {
var config = new Dictionary<string, ConfigValue>
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
await stack.SetAllConfigAsync(config); await stack.SetAllConfigAsync(config);
// pulumi up // pulumi up
@ -1131,7 +1121,7 @@ namespace Pulumi.Automation.Tests
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -1146,7 +1136,7 @@ namespace Pulumi.Automation.Tests
() => upTask); () => upTask);
} }
private class FileNotFoundStack : Pulumi.Stack private class FileNotFoundStack : Stack
{ {
public FileNotFoundStack() public FileNotFoundStack()
{ {
@ -1164,7 +1154,7 @@ namespace Pulumi.Automation.Tests
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -1182,7 +1172,7 @@ namespace Pulumi.Automation.Tests
[Fact] [Fact]
public async Task InlineProgramExceptionPropagatesToCallerWithServiceProvider() public async Task InlineProgramExceptionPropagatesToCallerWithServiceProvider()
{ {
using var provider = new ServiceCollection() await using var provider = new ServiceCollection()
.AddTransient<FileNotFoundStack>() // must be transient so it is instantiated each time .AddTransient<FileNotFoundStack>() // must be transient so it is instantiated each time
.BuildServiceProvider(); .BuildServiceProvider();
@ -1193,7 +1183,7 @@ namespace Pulumi.Automation.Tests
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program) using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -1225,10 +1215,11 @@ namespace Pulumi.Automation.Tests
// the semaphore because we want to alternately stutter // the semaphore because we want to alternately stutter
// programOne and programTwo so we can assert they aren't // programOne and programTwo so we can assert they aren't
// touching eachothers instances // touching eachothers instances
var config = new Pulumi.Config(); var config = new Config();
Assert.Equal(projectNameOne, Deployment.Instance.ProjectName); Assert.Equal(projectNameOne, Deployment.Instance.ProjectName);
Assert.Equal(stackNameOne, Deployment.Instance.StackName); Assert.Equal(stackNameOne, Deployment.Instance.StackName);
hasReachedSemaphoreOne = true; hasReachedSemaphoreOne = true;
// ReSharper disable once AccessToDisposedClosure
semaphoreOne.Wait(); semaphoreOne.Wait();
Assert.Equal(projectNameOne, Deployment.Instance.ProjectName); Assert.Equal(projectNameOne, Deployment.Instance.ProjectName);
Assert.Equal(stackNameOne, Deployment.Instance.StackName); Assert.Equal(stackNameOne, Deployment.Instance.StackName);
@ -1245,10 +1236,11 @@ namespace Pulumi.Automation.Tests
var programTwo = PulumiFn.Create(() => var programTwo = PulumiFn.Create(() =>
{ {
var config = new Pulumi.Config(); var config = new Config();
Assert.Equal(projectNameTwo, Deployment.Instance.ProjectName); Assert.Equal(projectNameTwo, Deployment.Instance.ProjectName);
Assert.Equal(stackNameTwo, Deployment.Instance.StackName); Assert.Equal(stackNameTwo, Deployment.Instance.StackName);
hasReachedSemaphoreTwo = true; hasReachedSemaphoreTwo = true;
// ReSharper disable once AccessToDisposedClosure
semaphoreTwo.Wait(); semaphoreTwo.Wait();
Assert.Equal(projectNameTwo, Deployment.Instance.ProjectName); Assert.Equal(projectNameTwo, Deployment.Instance.ProjectName);
Assert.Equal(stackNameTwo, Deployment.Instance.StackName); Assert.Equal(stackNameTwo, Deployment.Instance.StackName);
@ -1262,7 +1254,7 @@ namespace Pulumi.Automation.Tests
using var stackOne = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectNameOne, stackNameOne, programOne) using var stackOne = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectNameOne, stackNameOne, programOne)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
@ -1270,19 +1262,19 @@ namespace Pulumi.Automation.Tests
using var stackTwo = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectNameTwo, stackNameTwo, programTwo) using var stackTwo = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectNameTwo, stackNameTwo, programTwo)
{ {
EnvironmentVariables = new Dictionary<string, string?>() EnvironmentVariables = new Dictionary<string, string?>
{ {
["PULUMI_CONFIG_PASSPHRASE"] = "test", ["PULUMI_CONFIG_PASSPHRASE"] = "test",
} }
}); });
await stackOne.SetAllConfigAsync(new Dictionary<string, ConfigValue>() await stackOne.SetAllConfigAsync(new Dictionary<string, ConfigValue>
{ {
["bar"] = new ConfigValue("1"), ["bar"] = new ConfigValue("1"),
["buzz"] = new ConfigValue("1", isSecret: true), ["buzz"] = new ConfigValue("1", isSecret: true),
}); });
await stackTwo.SetAllConfigAsync(new Dictionary<string, ConfigValue>() await stackTwo.SetAllConfigAsync(new Dictionary<string, ConfigValue>
{ {
["bar"] = new ConfigValue("2"), ["bar"] = new ConfigValue("2"),
["buzz"] = new ConfigValue("2", isSecret: true), ["buzz"] = new ConfigValue("2", isSecret: true),
@ -1295,7 +1287,7 @@ namespace Pulumi.Automation.Tests
await Task.Delay(TimeSpan.FromSeconds(2)); await Task.Delay(TimeSpan.FromSeconds(2));
if (upTaskOne.IsFaulted) if (upTaskOne.IsFaulted)
throw upTaskOne.Exception!; throw upTaskOne.Exception!;
else if (upTaskOne.IsCompleted) if (upTaskOne.IsCompleted)
throw new Exception("Never hit semaphore in first UP task."); throw new Exception("Never hit semaphore in first UP task.");
} }
@ -1306,7 +1298,7 @@ namespace Pulumi.Automation.Tests
await Task.Delay(TimeSpan.FromSeconds(2)); await Task.Delay(TimeSpan.FromSeconds(2));
if (upTaskTwo.IsFaulted) if (upTaskTwo.IsFaulted)
throw upTaskTwo.Exception!; throw upTaskTwo.Exception!;
else if (upTaskTwo.IsCompleted) if (upTaskTwo.IsCompleted)
throw new Exception("Never hit semaphore in second UP task."); throw new Exception("Never hit semaphore in second UP task.");
} }
@ -1370,7 +1362,7 @@ namespace Pulumi.Automation.Tests
try try
{ {
Task.WaitAll(new Task[] { destroyTask, cancelTask }); Task.WaitAll(destroyTask, cancelTask);
} }
catch (AggregateException) catch (AggregateException)
{ {
@ -1422,8 +1414,8 @@ namespace Pulumi.Automation.Tests
var testMinVersion = SemVersion.Parse("2.21.1"); var testMinVersion = SemVersion.Parse("2.21.1");
if (errorExpected) if (errorExpected)
{ {
Action act = () => LocalWorkspace.ValidatePulumiVersion(testMinVersion, currentVersion, optOut); void ValidatePulumiVersion() => LocalWorkspace.ValidatePulumiVersion(testMinVersion, currentVersion, optOut);
Assert.Throws<InvalidOperationException>(act); Assert.Throws<InvalidOperationException>(ValidatePulumiVersion);
} }
else else
{ {

View file

@ -8,7 +8,7 @@ namespace Pulumi.Automation.Tests.Serialization
{ {
public class DynamicObjectTests public class DynamicObjectTests
{ {
private static LocalSerializer _serializer = new LocalSerializer(); private static readonly LocalSerializer _serializer = new LocalSerializer();
[Fact] [Fact]
public void Dynamic_With_YamlDotNet() public void Dynamic_With_YamlDotNet()

View file

@ -9,7 +9,7 @@ namespace Pulumi.Automation.Tests.Serialization
{ {
public class GeneralJsonConverterTests public class GeneralJsonConverterTests
{ {
private static LocalSerializer _serializer = new LocalSerializer(); private static readonly LocalSerializer _serializer = new LocalSerializer();
[Fact] [Fact]
public void CanDeserializeConfigValue() public void CanDeserializeConfigValue()

View file

@ -1,15 +1,21 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System;
using System.Text.Json; using System.Text.Json;
using Pulumi.Automation.Serialization; using Pulumi.Automation.Serialization;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace Pulumi.Automation.Tests.Serialization namespace Pulumi.Automation.Tests.Serialization
{ {
public class ProjectRuntimeJsonConverterTests public class ProjectRuntimeJsonConverterTests
{ {
private static LocalSerializer _serializer = new LocalSerializer(); private readonly ITestOutputHelper _output;
private static readonly LocalSerializer _serializer = new LocalSerializer();
public ProjectRuntimeJsonConverterTests(ITestOutputHelper output)
{
_output = output;
}
[Theory] [Theory]
[InlineData(ProjectRuntimeName.NodeJS)] [InlineData(ProjectRuntimeName.NodeJS)]
@ -71,7 +77,7 @@ namespace Pulumi.Automation.Tests.Serialization
var runtime = new ProjectRuntime(ProjectRuntimeName.Dotnet); var runtime = new ProjectRuntime(ProjectRuntimeName.Dotnet);
var json = _serializer.SerializeJson(runtime); var json = _serializer.SerializeJson(runtime);
Console.WriteLine(json); _output.WriteLine(json);
using var document = JsonDocument.Parse(json); using var document = JsonDocument.Parse(json);
Assert.NotNull(document); Assert.NotNull(document);
@ -91,7 +97,7 @@ namespace Pulumi.Automation.Tests.Serialization
}; };
var json = _serializer.SerializeJson(runtime); var json = _serializer.SerializeJson(runtime);
Console.WriteLine(json); _output.WriteLine(json);
using var document = JsonDocument.Parse(json); using var document = JsonDocument.Parse(json);
Assert.NotNull(document); Assert.NotNull(document);

View file

@ -4,12 +4,19 @@ using System;
using System.Text; using System.Text;
using Pulumi.Automation.Serialization; using Pulumi.Automation.Serialization;
using Xunit; using Xunit;
using Xunit.Abstractions;
namespace Pulumi.Automation.Tests.Serialization namespace Pulumi.Automation.Tests.Serialization
{ {
public class ProjectRuntimeYamlConverterTests public class ProjectRuntimeYamlConverterTests
{ {
private static LocalSerializer _serializer = new LocalSerializer(); private readonly ITestOutputHelper _output;
private static readonly LocalSerializer _serializer = new LocalSerializer();
public ProjectRuntimeYamlConverterTests(ITestOutputHelper output)
{
_output = output;
}
[Theory] [Theory]
[InlineData(ProjectRuntimeName.NodeJS)] [InlineData(ProjectRuntimeName.NodeJS)]
@ -67,7 +74,7 @@ runtime:
var runtime = new ProjectRuntime(ProjectRuntimeName.Dotnet); var runtime = new ProjectRuntime(ProjectRuntimeName.Dotnet);
var yaml = _serializer.SerializeYaml(runtime); var yaml = _serializer.SerializeYaml(runtime);
Console.WriteLine(yaml); _output.WriteLine(yaml);
Assert.Equal("dotnet" + Environment.NewLine, yaml); Assert.Equal("dotnet" + Environment.NewLine, yaml);
} }
@ -84,7 +91,7 @@ runtime:
}; };
var yaml = _serializer.SerializeYaml(runtime); var yaml = _serializer.SerializeYaml(runtime);
Console.WriteLine(yaml); _output.WriteLine(yaml);
var expected = new StringBuilder(); var expected = new StringBuilder();
expected.AppendLine("name: dotnet"); expected.AppendLine("name: dotnet");

View file

@ -9,7 +9,7 @@ namespace Pulumi.Automation.Tests.Serialization
{ {
public class StackSettingsConfigValueJsonConverterTests public class StackSettingsConfigValueJsonConverterTests
{ {
private static LocalSerializer _serializer = new LocalSerializer(); private static readonly LocalSerializer _serializer = new LocalSerializer();
[Fact] [Fact]
public void CanDeserializePlainString() public void CanDeserializePlainString()
@ -23,7 +23,7 @@ namespace Pulumi.Automation.Tests.Serialization
"; ";
var settings = _serializer.DeserializeJson<StackSettings>(json); var settings = _serializer.DeserializeJson<StackSettings>(json);
Assert.NotNull(settings?.Config); Assert.NotNull(settings.Config);
Assert.True(settings!.Config!.ContainsKey("test")); Assert.True(settings!.Config!.ContainsKey("test"));
var value = settings.Config["test"]; var value = settings.Config["test"];
@ -46,7 +46,7 @@ namespace Pulumi.Automation.Tests.Serialization
"; ";
var settings = _serializer.DeserializeJson<StackSettings>(json); var settings = _serializer.DeserializeJson<StackSettings>(json);
Assert.NotNull(settings?.Config); Assert.NotNull(settings.Config);
Assert.True(settings!.Config!.ContainsKey("test")); Assert.True(settings!.Config!.ContainsKey("test"));
var value = settings.Config["test"]; var value = settings.Config["test"];

View file

@ -9,7 +9,7 @@ namespace Pulumi.Automation.Tests.Serialization
{ {
public class StackSettingsConfigValueYamlConverterTests public class StackSettingsConfigValueYamlConverterTests
{ {
private static LocalSerializer _serializer = new LocalSerializer(); private static readonly LocalSerializer _serializer = new LocalSerializer();
[Fact] [Fact]
public void CanDeserializePlainString() public void CanDeserializePlainString()
@ -20,7 +20,7 @@ config:
"; ";
var settings = _serializer.DeserializeYaml<StackSettings>(yaml); var settings = _serializer.DeserializeYaml<StackSettings>(yaml);
Assert.NotNull(settings?.Config); Assert.NotNull(settings.Config);
Assert.True(settings!.Config!.ContainsKey("test")); Assert.True(settings!.Config!.ContainsKey("test"));
var value = settings.Config["test"]; var value = settings.Config["test"];
@ -39,7 +39,7 @@ config:
"; ";
var settings = _serializer.DeserializeYaml<StackSettings>(yaml); var settings = _serializer.DeserializeYaml<StackSettings>(yaml);
Assert.NotNull(settings?.Config); Assert.NotNull(settings.Config);
Assert.True(settings!.Config!.ContainsKey("test")); Assert.True(settings!.Config!.ContainsKey("test"));
var value = settings.Config["test"]; var value = settings.Config["test"];

View file

@ -5,18 +5,18 @@ using System.Collections.Generic;
namespace Pulumi.Automation.Collections namespace Pulumi.Automation.Collections
{ {
/// Compares two dictionaries for equality by content, as F# maps would. /// Compares two dictionaries for equality by content, as F# maps would.
internal sealed class DictionaryContentsComparer<K, V> : IEqualityComparer<IDictionary<K, V>> where K : notnull internal sealed class DictionaryContentsComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>> where TKey : notnull
{ {
private readonly IEqualityComparer<K> _keyComparer; private readonly IEqualityComparer<TKey> _keyComparer;
private readonly IEqualityComparer<V> _valueComparer; private readonly IEqualityComparer<TValue> _valueComparer;
public DictionaryContentsComparer(IEqualityComparer<K> keyComparer, IEqualityComparer<V> valueComparer) public DictionaryContentsComparer(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
{ {
this._keyComparer = keyComparer; this._keyComparer = keyComparer;
this._valueComparer = valueComparer; this._valueComparer = valueComparer;
} }
bool IEqualityComparer<IDictionary<K, V>>.Equals(IDictionary<K, V>? x, IDictionary<K, V>? y) bool IEqualityComparer<IDictionary<TKey, TValue>>.Equals(IDictionary<TKey, TValue>? x, IDictionary<TKey, TValue>? y)
{ {
if (x == null) if (x == null)
{ {
@ -24,7 +24,7 @@ namespace Pulumi.Automation.Collections
} }
if (y == null) if (y == null)
{ {
return x == null; return false;
} }
if (ReferenceEquals(x, y)) if (ReferenceEquals(x, y))
{ {
@ -34,7 +34,7 @@ namespace Pulumi.Automation.Collections
{ {
return false; return false;
} }
var y2 = new Dictionary<K, V>(y, this._keyComparer); var y2 = new Dictionary<TKey, TValue>(y, this._keyComparer);
foreach (var pair in x) foreach (var pair in x)
{ {
if (!y2.ContainsKey(pair.Key)) if (!y2.ContainsKey(pair.Key))
@ -50,7 +50,7 @@ namespace Pulumi.Automation.Collections
return true; return true;
} }
int IEqualityComparer<IDictionary<K, V>>.GetHashCode(IDictionary<K, V> obj) int IEqualityComparer<IDictionary<TKey, TValue>>.GetHashCode(IDictionary<TKey, TValue> obj)
{ {
return 0; // inefficient but correct return 0; // inefficient but correct
} }

View file

@ -20,14 +20,14 @@ namespace Pulumi.Automation.Commands.Exceptions
this.Name = name; this.Name = name;
} }
private static readonly Regex NotFoundRegexPattern = new Regex("no stack named.*found"); private static readonly Regex _notFoundRegexPattern = new Regex("no stack named.*found");
private static readonly Regex AlreadyExistsRegexPattern = new Regex("stack.*already exists"); private static readonly Regex _alreadyExistsRegexPattern = new Regex("stack.*already exists");
private static readonly string ConflictText = "[409] Conflict: Another update is currently in progress."; private static readonly string _conflictText = "[409] Conflict: Another update is currently in progress.";
internal static CommandException CreateFromResult(CommandResult result) internal static CommandException CreateFromResult(CommandResult result)
=> NotFoundRegexPattern.IsMatch(result.StandardError) ? new StackNotFoundException(result) => _notFoundRegexPattern.IsMatch(result.StandardError) ? new StackNotFoundException(result)
: AlreadyExistsRegexPattern.IsMatch(result.StandardError) ? new StackAlreadyExistsException(result) : _alreadyExistsRegexPattern.IsMatch(result.StandardError) ? new StackAlreadyExistsException(result)
: result.StandardError?.IndexOf(ConflictText) >= 0 ? new ConcurrentUpdateException(result) : result.StandardError.IndexOf(_conflictText, StringComparison.Ordinal) >= 0 ? new ConcurrentUpdateException(result)
: new CommandException(result); : new CommandException(result);
} }
} }

View file

@ -11,7 +11,7 @@ namespace Pulumi.Automation.Commands
internal interface IPulumiCmd internal interface IPulumiCmd
{ {
Task<CommandResult> RunAsync( Task<CommandResult> RunAsync(
IEnumerable<string> args, IList<string> args,
string workingDir, string workingDir,
IDictionary<string, string?> additionalEnv, IDictionary<string, string?> additionalEnv,
Action<string>? onStandardOutput = null, Action<string>? onStandardOutput = null,

View file

@ -9,7 +9,6 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using CliWrap; using CliWrap;
using Pulumi.Automation.Commands.Exceptions; using Pulumi.Automation.Commands.Exceptions;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
@ -20,7 +19,7 @@ namespace Pulumi.Automation.Commands
{ {
public async Task<CommandResult> RunAsync( public async Task<CommandResult> RunAsync(
IEnumerable<string> args, IList<string> args,
string workingDir, string workingDir,
IDictionary<string, string?> additionalEnv, IDictionary<string, string?> additionalEnv,
Action<string>? onStandardOutput = null, Action<string>? onStandardOutput = null,
@ -40,14 +39,11 @@ namespace Pulumi.Automation.Commands
await eventLogWatcher.Stop(); await eventLogWatcher.Stop();
} }
} }
else return await RunAsyncInner(args, workingDir, additionalEnv, onStandardOutput, onStandardError, eventLogFile: null, cancellationToken);
{
return await RunAsyncInner(args, workingDir, additionalEnv, onStandardOutput, onStandardError, eventLogFile: null, cancellationToken);
}
} }
private async Task<CommandResult> RunAsyncInner( private async Task<CommandResult> RunAsyncInner(
IEnumerable<string> args, IList<string> args,
string workingDir, string workingDir,
IDictionary<string, string?> additionalEnv, IDictionary<string, string?> additionalEnv,
Action<string>? onStandardOutput = null, Action<string>? onStandardOutput = null,
@ -88,10 +84,7 @@ namespace Pulumi.Automation.Commands
{ {
throw CommandException.CreateFromResult(result); throw CommandException.CreateFromResult(result);
} }
else return result;
{
return result;
}
} }
private static IReadOnlyDictionary<string, string?> PulumiEnvironment(IDictionary<string, string?> additionalEnv, bool debugCommands) private static IReadOnlyDictionary<string, string?> PulumiEnvironment(IDictionary<string, string?> additionalEnv, bool debugCommands)
@ -108,18 +101,18 @@ namespace Pulumi.Automation.Commands
return env; return env;
} }
private static IEnumerable<string> PulumiArgs(IEnumerable<string> args, EventLogFile? eventLogFile) private static IList<string> PulumiArgs(IList<string> args, EventLogFile? eventLogFile)
{ {
// all commands should be run in non-interactive mode. // all commands should be run in non-interactive mode.
// this causes commands to fail rather than prompting for input (and thus hanging indefinitely) // this causes commands to fail rather than prompting for input (and thus hanging indefinitely)
if (!args.Contains("--non-interactive")) if (!args.Contains("--non-interactive"))
{ {
args = args.Concat(new[] { "--non-interactive" }); args = args.Concat(new[] { "--non-interactive" }).ToList();
} }
if (eventLogFile != null) if (eventLogFile != null)
{ {
args = args.Concat(new[] { "--event-log", eventLogFile.FilePath }); args = args.Concat(new[] { "--event-log", eventLogFile.FilePath }).ToList();
} }
return args; return args;
@ -132,15 +125,12 @@ namespace Pulumi.Automation.Commands
{ {
return "event-log"; return "event-log";
} }
else return alphaNumWord.IsMatch(firstArgument) ? firstArgument : "event-log";
{
return alphaNumWord.IsMatch(firstArgument) ? firstArgument : "event-log";
}
} }
private class EventLogFile : IDisposable private sealed class EventLogFile : IDisposable
{ {
private bool _disposedValue; public string FilePath { get; }
public EventLogFile(string command) public EventLogFile(string command)
{ {
@ -149,37 +139,21 @@ namespace Pulumi.Automation.Commands
this.FilePath = Path.Combine(logDir, "eventlog.txt"); this.FilePath = Path.Combine(logDir, "eventlog.txt");
} }
public string FilePath { get; }
protected virtual void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
var dir = Path.GetDirectoryName(this.FilePath);
try
{
Directory.Delete(dir, recursive: true);
}
catch (Exception e)
{
// allow graceful exit if for some reason
// we're not able to delete the directory
// will rely on OS to clean temp directory
// in this case.
Trace.TraceWarning("Ignoring exception during cleanup of {0} folder: {1}", dir, e);
}
}
_disposedValue = true;
}
}
public void Dispose() public void Dispose()
{ {
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method var dir = Path.GetDirectoryName(this.FilePath);
Dispose(disposing: true); try
GC.SuppressFinalize(this); {
Directory.Delete(dir, recursive: true);
}
catch (Exception e)
{
// allow graceful exit if for some reason
// we're not able to delete the directory
// will rely on OS to clean temp directory
// in this case.
Trace.TraceWarning("Ignoring exception during cleanup of {0} folder: {1}", dir, e);
}
} }
} }
} }

View file

@ -1,7 +1,5 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System;
namespace Pulumi.Automation namespace Pulumi.Automation
{ {
/// <summary> /// <summary>

View file

@ -8,14 +8,14 @@ using Pulumi.Automation.Serialization;
namespace Pulumi.Automation.Events namespace Pulumi.Automation.Events
{ {
internal class EventLogWatcher : IDisposable internal sealed class EventLogWatcher : IDisposable
{ {
private readonly LocalSerializer _localSerializer = new LocalSerializer(); private readonly LocalSerializer _localSerializer = new LocalSerializer();
private readonly Action<EngineEvent> _onEvent; private readonly Action<EngineEvent> _onEvent;
private const int _pollingIntervalMilliseconds = 100; private const int _pollingIntervalMilliseconds = 100;
// We keep track of the last position in the file. // We keep track of the last position in the file.
private long _position = 0; private long _position;
public string LogFile { get; } public string LogFile { get; }
private readonly Task _pollingTask; private readonly Task _pollingTask;
private readonly CancellationTokenSource _internalCancellationTokenSource = new CancellationTokenSource(); private readonly CancellationTokenSource _internalCancellationTokenSource = new CancellationTokenSource();
@ -82,6 +82,7 @@ namespace Pulumi.Automation.Events
await ReadEventsOnce(); await ReadEventsOnce();
await Task.Delay(_pollingIntervalMilliseconds, linkedSource.Token); await Task.Delay(_pollingIntervalMilliseconds, linkedSource.Token);
} }
// ReSharper disable once FunctionNeverReturns
} }
private async Task ReadEventsOnce() private async Task ReadEventsOnce()
@ -91,15 +92,11 @@ namespace Pulumi.Automation.Events
return; return;
} }
using var fs = new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) await using var fs = new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) { Position = this._position };
{
Position = this._position
};
using var reader = new StreamReader(fs); using var reader = new StreamReader(fs);
string? line;
while (reader.Peek() >= 0) while (reader.Peek() >= 0)
{ {
line = await reader.ReadLineAsync(); var line = await reader.ReadLineAsync();
this._position = fs.Position; this._position = fs.Position;
if (!string.IsNullOrWhiteSpace(line)) if (!string.IsNullOrWhiteSpace(line))
{ {

View file

@ -1,10 +1,12 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Events;
namespace Pulumi.Automation.Exceptions namespace Pulumi.Automation.Exceptions
{ {
public sealed class NoSummaryEventException : MissingExpectedEventException public sealed class NoSummaryEventException : MissingExpectedEventException
{ {
internal NoSummaryEventException(string? message) : base(nameof(Events.SummaryEvent), message) internal NoSummaryEventException(string? message) : base(nameof(SummaryEvent), message)
{ {
} }
} }

View file

@ -1,16 +1,16 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Semver;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Pulumi.Automation.Commands; using Pulumi.Automation.Commands;
using Pulumi.Automation.Exceptions;
using Pulumi.Automation.Serialization; using Pulumi.Automation.Serialization;
using Semver;
namespace Pulumi.Automation namespace Pulumi.Automation
{ {
@ -43,9 +43,9 @@ namespace Pulumi.Automation
/// <inheritdoc/> /// <inheritdoc/>
public override string? PulumiHome { get; } public override string? PulumiHome { get; }
private SemVersion? pulumiVersion; private SemVersion? _pulumiVersion;
/// <inheritdoc/> /// <inheritdoc/>
public override string PulumiVersion => pulumiVersion?.ToString() ?? throw new InvalidOperationException("Failed to get Pulumi version."); public override string PulumiVersion => _pulumiVersion?.ToString() ?? throw new InvalidOperationException("Failed to get Pulumi version.");
/// <inheritdoc/> /// <inheritdoc/>
public override string? SecretsProvider { get; } public override string? SecretsProvider { get; }
@ -358,11 +358,11 @@ namespace Pulumi.Automation
!ProjectSettings.Comparer.Equals(projectSettings, existingSettings)) !ProjectSettings.Comparer.Equals(projectSettings, existingSettings))
{ {
var path = this.FindSettingsFile(); var path = this.FindSettingsFile();
throw new Exceptions.ProjectSettingsConflictException(path); throw new ProjectSettingsConflictException(path);
} }
} }
private static readonly string[] SettingsExtensions = new string[] { ".yaml", ".yml", ".json" }; private static readonly string[] _settingsExtensions = { ".yaml", ".yml", ".json" };
private async Task PopulatePulumiVersionAsync(CancellationToken cancellationToken) private async Task PopulatePulumiVersionAsync(CancellationToken cancellationToken)
{ {
@ -376,8 +376,8 @@ namespace Pulumi.Automation
var skipVersionCheckVar = "PULUMI_AUTOMATION_API_SKIP_VERSION_CHECK"; var skipVersionCheckVar = "PULUMI_AUTOMATION_API_SKIP_VERSION_CHECK";
var hasSkipEnvVar = this.EnvironmentVariables?.ContainsKey(skipVersionCheckVar) ?? false; var hasSkipEnvVar = this.EnvironmentVariables?.ContainsKey(skipVersionCheckVar) ?? false;
var optOut = hasSkipEnvVar || Environment.GetEnvironmentVariable(skipVersionCheckVar) != null; var optOut = hasSkipEnvVar || Environment.GetEnvironmentVariable(skipVersionCheckVar) != null;
LocalWorkspace.ValidatePulumiVersion(LocalWorkspace._minimumVersion, version, optOut); ValidatePulumiVersion(_minimumVersion, version, optOut);
this.pulumiVersion = version; this._pulumiVersion = version;
} }
internal static void ValidatePulumiVersion(SemVersion minVersion, SemVersion currentVersion, bool optOut) internal static void ValidatePulumiVersion(SemVersion minVersion, SemVersion currentVersion, bool optOut)
@ -410,12 +410,8 @@ namespace Pulumi.Automation
{ {
return this._serializer.DeserializeJson<ProjectSettings>(content); return this._serializer.DeserializeJson<ProjectSettings>(content);
} }
else var model = this._serializer.DeserializeYaml<ProjectSettingsModel>(content);
{ return model.Convert();
var model = this._serializer.DeserializeYaml<ProjectSettingsModel>(content);
return model.Convert();
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -429,7 +425,7 @@ namespace Pulumi.Automation
private string FindSettingsFile() private string FindSettingsFile()
{ {
foreach (var ext in SettingsExtensions) foreach (var ext in _settingsExtensions)
{ {
var testPath = Path.Combine(this.WorkDir, $"Pulumi{ext}"); var testPath = Path.Combine(this.WorkDir, $"Pulumi{ext}");
if (File.Exists(testPath)) if (File.Exists(testPath))
@ -455,7 +451,7 @@ namespace Pulumi.Automation
{ {
var settingsName = GetStackSettingsName(stackName); var settingsName = GetStackSettingsName(stackName);
foreach (var ext in SettingsExtensions) foreach (var ext in _settingsExtensions)
{ {
var isJson = ext == ".json"; var isJson = ext == ".json";
var path = Path.Combine(this.WorkDir, $"Pulumi.{settingsName}{ext}"); var path = Path.Combine(this.WorkDir, $"Pulumi.{settingsName}{ext}");
@ -475,7 +471,7 @@ namespace Pulumi.Automation
var settingsName = GetStackSettingsName(stackName); var settingsName = GetStackSettingsName(stackName);
var foundExt = ".yaml"; var foundExt = ".yaml";
foreach (var ext in SettingsExtensions) foreach (var ext in _settingsExtensions)
{ {
var testPath = Path.Combine(this.WorkDir, $"Pulumi.{settingsName}{ext}"); var testPath = Path.Combine(this.WorkDir, $"Pulumi.{settingsName}{ext}");
if (File.Exists(testPath)) if (File.Exists(testPath))
@ -567,7 +563,7 @@ namespace Pulumi.Automation
/// <inheritdoc/> /// <inheritdoc/>
public override Task CreateStackAsync(string stackName, CancellationToken cancellationToken) public override Task CreateStackAsync(string stackName, CancellationToken cancellationToken)
{ {
var args = new List<string>() var args = new List<string>
{ {
"stack", "stack",
"init", "init",
@ -614,7 +610,7 @@ namespace Pulumi.Automation
var tempFileName = Path.GetTempFileName(); var tempFileName = Path.GetTempFileName();
try try
{ {
File.WriteAllText(tempFileName, state.Json.GetRawText()); await File.WriteAllTextAsync(tempFileName, state.Json.GetRawText(), cancellationToken);
await this.RunCommandAsync(new[] { "stack", "import", "--file", tempFileName, "--stack", stackName }, await this.RunCommandAsync(new[] { "stack", "import", "--file", tempFileName, "--stack", stackName },
cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
} }
@ -631,7 +627,7 @@ namespace Pulumi.Automation
/// <inheritdoc/> /// <inheritdoc/>
public override Task RemovePluginAsync(string? name = null, string? versionRange = null, PluginKind kind = PluginKind.Resource, CancellationToken cancellationToken = default) public override Task RemovePluginAsync(string? name = null, string? versionRange = null, PluginKind kind = PluginKind.Resource, CancellationToken cancellationToken = default)
{ {
var args = new List<string>() var args = new List<string>
{ {
"plugin", "plugin",
"rm", "rm",

View file

@ -25,7 +25,7 @@ namespace Pulumi.Automation
if (y == null) if (y == null)
{ {
return x == null; return false;
} }
if (ReferenceEquals(x, y)) if (ReferenceEquals(x, y))

View file

@ -32,7 +32,7 @@ namespace Pulumi.Automation
if (y == null) if (y == null)
{ {
return x == null; return false;
} }
if (ReferenceEquals(x, y)) if (ReferenceEquals(x, y))

View file

@ -7,6 +7,7 @@ namespace Pulumi.Automation
/// </summary> /// </summary>
public enum ProjectRuntimeName public enum ProjectRuntimeName
{ {
// ReSharper disable once InconsistentNaming
NodeJS, NodeJS,
Go, Go,
Python, Python,

View file

@ -46,7 +46,7 @@ namespace Pulumi.Automation
if (y == null) if (y == null)
{ {
return x == null; return false;
} }
if (ReferenceEquals(x, y)) if (ReferenceEquals(x, y))

View file

@ -48,17 +48,14 @@ namespace Pulumi.Automation
{ {
} }
internal static ProjectSettings Default(string name) { internal static ProjectSettings Default(string name) =>
var defaultSettings = new ProjectSettings(name, new ProjectRuntime(ProjectRuntimeName.Dotnet)); new ProjectSettings(name, new ProjectRuntime(ProjectRuntimeName.Dotnet)) { Main = Directory.GetCurrentDirectory() };
defaultSettings.Main = Directory.GetCurrentDirectory();
return defaultSettings;
}
internal bool IsDefault internal bool IsDefault
{ {
get get
{ {
return ProjectSettings.Comparer.Equals(this, ProjectSettings.Default(this.Name)); return Comparer.Equals(this, Default(this.Name));
} }
} }
@ -73,7 +70,7 @@ namespace Pulumi.Automation
if (y == null) if (y == null)
{ {
return x == null; return false;
} }
if (ReferenceEquals(x, y)) if (ReferenceEquals(x, y))

View file

@ -24,7 +24,7 @@ namespace Pulumi.Automation
private sealed class ProjectTemplateComparer : IEqualityComparer<ProjectTemplate> private sealed class ProjectTemplateComparer : IEqualityComparer<ProjectTemplate>
{ {
private IEqualityComparer<IDictionary<string, ProjectTemplateConfigValue>> _configComparer = private readonly IEqualityComparer<IDictionary<string, ProjectTemplateConfigValue>> _configComparer =
new DictionaryContentsComparer<string, ProjectTemplateConfigValue>( new DictionaryContentsComparer<string, ProjectTemplateConfigValue>(
EqualityComparer<string>.Default, EqualityComparer<string>.Default,
ProjectTemplateConfigValue.Comparer); ProjectTemplateConfigValue.Comparer);
@ -38,7 +38,7 @@ namespace Pulumi.Automation
if (y == null) if (y == null)
{ {
return x == null; return false;
} }
if (ReferenceEquals(x, y)) if (ReferenceEquals(x, y))

View file

@ -29,7 +29,7 @@ namespace Pulumi.Automation
if (y == null) if (y == null)
{ {
return x == null; return false;
} }
if (ReferenceEquals(x, y)) if (ReferenceEquals(x, y))

View file

@ -1,6 +1,5 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;

View file

@ -23,7 +23,7 @@ namespace Pulumi.Automation
if (stackType is null) if (stackType is null)
throw new ArgumentNullException(nameof(stackType)); throw new ArgumentNullException(nameof(stackType));
var pulumiStackType = typeof(Pulumi.Stack); var pulumiStackType = typeof(Stack);
if (!pulumiStackType.IsAssignableFrom(stackType) || pulumiStackType == stackType) if (!pulumiStackType.IsAssignableFrom(stackType) || pulumiStackType == stackType)
throw new ArgumentException($"Provided stack type must derive from {pulumiStackType.FullName}.", nameof(stackType)); throw new ArgumentException($"Provided stack type must derive from {pulumiStackType.FullName}.", nameof(stackType));
@ -42,7 +42,7 @@ namespace Pulumi.Automation
if (this._serviceProvider is null) if (this._serviceProvider is null)
throw new ArgumentNullException(nameof(this._serviceProvider), $"The provided service provider was null by the time this {nameof(PulumiFn)} was invoked."); throw new ArgumentNullException(nameof(this._serviceProvider), $"The provided service provider was null by the time this {nameof(PulumiFn)} was invoked.");
return this._serviceProvider.GetService(this._stackType) as Pulumi.Stack return this._serviceProvider.GetService(this._stackType) as Stack
?? throw new ApplicationException( ?? throw new ApplicationException(
$"Failed to resolve instance of type {this._stackType.FullName} from service provider. Register the type with the service provider before this {nameof(PulumiFn)} is invoked."); $"Failed to resolve instance of type {this._stackType.FullName} from service provider. Register the type with the service provider before this {nameof(PulumiFn)} is invoked.");
} }

View file

@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace Pulumi.Automation namespace Pulumi.Automation
{ {
internal class PulumiFn<TStack> : PulumiFn where TStack : Pulumi.Stack internal class PulumiFn<TStack> : PulumiFn where TStack : Stack
{ {
private readonly Func<TStack> _stackFactory; private readonly Func<TStack> _stackFactory;

View file

@ -40,13 +40,13 @@ namespace Pulumi.Automation
/// <param name="program">An asynchronous pulumi program that takes in a <see cref="CancellationToken"/>.</param> /// <param name="program">An asynchronous pulumi program that takes in a <see cref="CancellationToken"/>.</param>
public static PulumiFn Create(Func<CancellationToken, Task> program) public static PulumiFn Create(Func<CancellationToken, Task> program)
{ {
Func<CancellationToken, Task<IDictionary<string, object?>>> wrapper = async cancellationToken => async Task<IDictionary<string, object?>> Wrapper(CancellationToken cancellationToken)
{ {
await program(cancellationToken).ConfigureAwait(false); await program(cancellationToken).ConfigureAwait(false);
return ImmutableDictionary<string, object?>.Empty; return ImmutableDictionary<string, object?>.Empty;
}; }
return new PulumiFnInline(wrapper); return new PulumiFnInline(Wrapper);
} }
/// <summary> /// <summary>
@ -55,13 +55,13 @@ namespace Pulumi.Automation
/// <param name="program">An asynchronous pulumi program.</param> /// <param name="program">An asynchronous pulumi program.</param>
public static PulumiFn Create(Func<Task> program) public static PulumiFn Create(Func<Task> program)
{ {
Func<CancellationToken, Task<IDictionary<string, object?>>> wrapper = async cancellationToken => async Task<IDictionary<string, object?>> Wrapper(CancellationToken cancellationToken)
{ {
await program().ConfigureAwait(false); await program().ConfigureAwait(false);
return ImmutableDictionary<string, object?>.Empty; return ImmutableDictionary<string, object?>.Empty;
}; }
return new PulumiFnInline(wrapper); return new PulumiFnInline(Wrapper);
} }
/// <summary> /// <summary>
@ -70,13 +70,13 @@ namespace Pulumi.Automation
/// <param name="program">A pulumi program that returns an output.</param> /// <param name="program">A pulumi program that returns an output.</param>
public static PulumiFn Create(Func<IDictionary<string, object?>> program) public static PulumiFn Create(Func<IDictionary<string, object?>> program)
{ {
Func<CancellationToken, Task<IDictionary<string, object?>>> wrapper = cancellationToken => Task<IDictionary<string, object?>> Wrapper(CancellationToken cancellationToken)
{ {
var output = program(); var output = program();
return Task.FromResult(output); return Task.FromResult(output);
}; }
return new PulumiFnInline(wrapper); return new PulumiFnInline(Wrapper);
} }
/// <summary> /// <summary>
@ -91,7 +91,7 @@ namespace Pulumi.Automation
/// </summary> /// </summary>
/// <typeparam name="TStack">The <see cref="Pulumi.Stack"/> type.</typeparam> /// <typeparam name="TStack">The <see cref="Pulumi.Stack"/> type.</typeparam>
public static PulumiFn Create<TStack>() public static PulumiFn Create<TStack>()
where TStack : Pulumi.Stack, new() where TStack : Stack, new()
=> new PulumiFn<TStack>(() => new TStack()); => new PulumiFn<TStack>(() => new TStack());
/// <summary> /// <summary>
@ -104,7 +104,7 @@ namespace Pulumi.Automation
/// <typeparam name="TStack">The <see cref="Pulumi.Stack"/> type.</typeparam> /// <typeparam name="TStack">The <see cref="Pulumi.Stack"/> type.</typeparam>
/// <param name="serviceProvider">The service provider that will be used to resolve an instance of <typeparamref name="TStack"/>.</param> /// <param name="serviceProvider">The service provider that will be used to resolve an instance of <typeparamref name="TStack"/>.</param>
public static PulumiFn Create<TStack>(IServiceProvider serviceProvider) public static PulumiFn Create<TStack>(IServiceProvider serviceProvider)
where TStack : Pulumi.Stack where TStack : Stack
=> new PulumiFnServiceProvider(serviceProvider, typeof(TStack)); => new PulumiFnServiceProvider(serviceProvider, typeof(TStack));
/// <summary> /// <summary>

View file

@ -1,7 +1,5 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System;
namespace Pulumi.Automation namespace Pulumi.Automation
{ {
/// <summary> /// <summary>

View file

@ -1,7 +1,5 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using System.Threading; using System.Threading;
@ -56,6 +54,7 @@ namespace Pulumi.Automation
this._callerContext.ExceptionDispatchInfo = await Deployment.RunInlineAsync( this._callerContext.ExceptionDispatchInfo = await Deployment.RunInlineAsync(
settings, settings,
// ReSharper disable once AccessToDisposedClosure
runner => this._callerContext.Program.InvokeAsync(runner, cts.Token)) runner => this._callerContext.Program.InvokeAsync(runner, cts.Token))
.ConfigureAwait(false); .ConfigureAwait(false);

View file

@ -1,7 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,7 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -23,7 +23,7 @@ namespace Pulumi.Automation.Serialization.Json
if (reader.TokenType == JsonTokenType.Number) if (reader.TokenType == JsonTokenType.Number)
{ {
if (reader.TryGetInt64(out long l)) if (reader.TryGetInt64(out var l))
{ {
return l; return l;
} }
@ -33,7 +33,7 @@ namespace Pulumi.Automation.Serialization.Json
if (reader.TokenType == JsonTokenType.String) if (reader.TokenType == JsonTokenType.String)
{ {
if (reader.TryGetDateTime(out DateTime datetime)) if (reader.TryGetDateTime(out var datetime))
{ {
return datetime; return datetime;
} }

View file

@ -1,7 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,8 +1,8 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic; using System.Collections.Generic;
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,7 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,7 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,7 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,7 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,7 +1,7 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,9 +1,9 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic; using System.Collections.Generic;
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events;
using System.Linq; using System.Linq;
using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,8 +1,8 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic; using System.Collections.Generic;
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go

View file

@ -1,8 +1,8 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic; using System.Collections.Generic;
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
using Pulumi.Automation.Serialization.Json;
// NOTE: The classes in this file are intended to align with the serialized // NOTE: The classes in this file are intended to align with the serialized
// JSON types defined and versioned in sdk/go/common/apitype/events.go // JSON types defined and versioned in sdk/go/common/apitype/events.go
@ -22,7 +22,9 @@ namespace Pulumi.Automation.Serialization
new SummaryEvent( new SummaryEvent(
this.MaybeCorrupt, this.MaybeCorrupt,
this.DurationSeconds, this.DurationSeconds,
// ReSharper disable once ConstantNullCoalescingCondition
this.ResourceChanges ?? new Dictionary<OperationType, int>(), this.ResourceChanges ?? new Dictionary<OperationType, int>(),
// ReSharper disable once ConstantNullCoalescingCondition
this.PolicyPacks ?? new Dictionary<string, string>()); this.PolicyPacks ?? new Dictionary<string, string>());
} }
} }

View file

@ -11,9 +11,9 @@ namespace Pulumi.Automation.Serialization.Yaml
{ {
internal class ProjectRuntimeOptionsYamlConverter : IYamlTypeConverter internal class ProjectRuntimeOptionsYamlConverter : IYamlTypeConverter
{ {
private static readonly Type Type = typeof(ProjectRuntimeOptions); private static readonly Type _type = typeof(ProjectRuntimeOptions);
private static readonly List<string> PropertyNames = typeof(ProjectRuntimeOptions).GetProperties().Select(x => x.Name).ToList(); private static readonly List<string> _propertyNames = typeof(ProjectRuntimeOptions).GetProperties().Select(x => x.Name).ToList();
private static readonly Dictionary<string, Func<Scalar, string, Type, object?>> Readers = private static readonly Dictionary<string, Func<Scalar, string, Type, object?>> _readers =
new Dictionary<string, Func<Scalar, string, Type, object?>>(StringComparer.OrdinalIgnoreCase) new Dictionary<string, Func<Scalar, string, Type, object?>>(StringComparer.OrdinalIgnoreCase)
{ {
[nameof(ProjectRuntimeOptions.Binary)] = (x, p, t) => x.Value, [nameof(ProjectRuntimeOptions.Binary)] = (x, p, t) => x.Value,
@ -21,22 +21,21 @@ namespace Pulumi.Automation.Serialization.Yaml
[nameof(ProjectRuntimeOptions.VirtualEnv)] = (x, p, t) => x.Value, [nameof(ProjectRuntimeOptions.VirtualEnv)] = (x, p, t) => x.Value,
}; };
public bool Accepts(Type type) public bool Accepts(Type type) => type == _type;
=> type == Type;
public object? ReadYaml(IParser parser, Type type) public object ReadYaml(IParser parser, Type type)
{ {
if (!parser.TryConsume<MappingStart>(out _)) if (!parser.TryConsume<MappingStart>(out _))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting object."); throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting object.");
var values = PropertyNames.ToDictionary(x => x, x => (object?)null, StringComparer.OrdinalIgnoreCase); var values = _propertyNames.ToDictionary(x => x, x => (object?)null, StringComparer.OrdinalIgnoreCase);
do do
{ {
if (!parser.TryConsume<Scalar>(out var propertyNameScalar)) if (!parser.TryConsume<Scalar>(out var propertyNameScalar))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting a property name."); throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting a property name.");
if (!Readers.TryGetValue(propertyNameScalar.Value, out var readerFunc)) if (!_readers.TryGetValue(propertyNameScalar.Value, out var readerFunc))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Invalid property [{propertyNameScalar.Value}]."); throw new YamlException($"Unable to deserialize [{type.FullName}]. Invalid property [{propertyNameScalar.Value}].");
if (!parser.TryConsume<Scalar>(out var propertyValueScalar)) if (!parser.TryConsume<Scalar>(out var propertyValueScalar))

View file

@ -9,15 +9,14 @@ namespace Pulumi.Automation.Serialization.Yaml
{ {
internal class ProjectRuntimeYamlConverter : IYamlTypeConverter internal class ProjectRuntimeYamlConverter : IYamlTypeConverter
{ {
private static readonly Type Type = typeof(ProjectRuntime); private static readonly Type _type = typeof(ProjectRuntime);
private static readonly Type OptionsType = typeof(ProjectRuntimeOptions); private static readonly Type _optionsType = typeof(ProjectRuntimeOptions);
private readonly IYamlTypeConverter _optionsConverter = new ProjectRuntimeOptionsYamlConverter(); private readonly IYamlTypeConverter _optionsConverter = new ProjectRuntimeOptionsYamlConverter();
public bool Accepts(Type type) public bool Accepts(Type type) => type == _type;
=> type == Type;
public object? ReadYaml(IParser parser, Type type) public object ReadYaml(IParser parser, Type type)
{ {
if (parser.TryConsume<Scalar>(out var nameValueScalar)) if (parser.TryConsume<Scalar>(out var nameValueScalar))
{ {
@ -51,7 +50,7 @@ namespace Pulumi.Automation.Serialization.Yaml
if (!parser.Accept<MappingStart>(out _)) if (!parser.Accept<MappingStart>(out _))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Runtime options property should be an object."); throw new YamlException($"Unable to deserialize [{type.FullName}]. Runtime options property should be an object.");
var runtimeOptionsObj = this._optionsConverter.ReadYaml(parser, OptionsType); var runtimeOptionsObj = this._optionsConverter.ReadYaml(parser, _optionsType);
if (!(runtimeOptionsObj is ProjectRuntimeOptions runtimeOptions)) if (!(runtimeOptionsObj is ProjectRuntimeOptions runtimeOptions))
throw new YamlException("There was an issue deserializing the runtime options object."); throw new YamlException("There was an issue deserializing the runtime options object.");
@ -87,7 +86,7 @@ namespace Pulumi.Automation.Serialization.Yaml
emitter.Emit(new Scalar(runtime.Name.ToString().ToLower())); emitter.Emit(new Scalar(runtime.Name.ToString().ToLower()));
emitter.Emit(new Scalar("options")); emitter.Emit(new Scalar("options"));
this._optionsConverter.WriteYaml(emitter, runtime.Options, OptionsType); this._optionsConverter.WriteYaml(emitter, runtime.Options, _optionsType);
emitter.Emit(new MappingEnd()); emitter.Emit(new MappingEnd());
} }

View file

@ -9,12 +9,11 @@ namespace Pulumi.Automation.Serialization.Yaml
{ {
internal class StackSettingsConfigValueYamlConverter : IYamlTypeConverter internal class StackSettingsConfigValueYamlConverter : IYamlTypeConverter
{ {
private static readonly Type Type = typeof(StackSettingsConfigValue); private static readonly Type _type = typeof(StackSettingsConfigValue);
public bool Accepts(Type type) public bool Accepts(Type type) => type == _type;
=> type == Type;
public object? ReadYaml(IParser parser, Type type) public object ReadYaml(IParser parser, Type type)
{ {
// check if plain string // check if plain string
if (parser.Accept<Scalar>(out var stringValue)) if (parser.Accept<Scalar>(out var stringValue))
@ -43,10 +42,7 @@ namespace Pulumi.Automation.Serialization.Yaml
parser.MoveNext(); parser.MoveNext();
return new StackSettingsConfigValue(securePropertyValue.Value, true); return new StackSettingsConfigValue(securePropertyValue.Value, true);
} }
else throw new NotSupportedException("Automation API does not currently support deserializing complex objects from stack settings.");
{
throw new NotSupportedException("Automation API does not currently support deserializing complex objects from stack settings.");
}
} }
public void WriteYaml(IEmitter emitter, object? value, Type type) public void WriteYaml(IEmitter emitter, object? value, Type type)

View file

@ -1,6 +1,8 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization;
using YamlDotNet.Serialization;
namespace Pulumi.Automation namespace Pulumi.Automation
{ {
@ -9,8 +11,8 @@ namespace Pulumi.Automation
/// <summary> /// <summary>
/// This stack's secrets provider. /// This stack's secrets provider.
/// </summary> /// </summary>
[YamlDotNet.Serialization.YamlMember(Alias="secretsprovider")] [YamlMember(Alias="secretsprovider")]
[System.Text.Json.Serialization.JsonPropertyName("secretsprovider")] [JsonPropertyName("secretsprovider")]
public string? SecretsProvider { get; set; } public string? SecretsProvider { get; set; }
@ -18,8 +20,8 @@ namespace Pulumi.Automation
/// This is the KMS-encrypted ciphertext for the data key used for secrets /// This is the KMS-encrypted ciphertext for the data key used for secrets
/// encryption. Only used for cloud-based secrets providers. /// encryption. Only used for cloud-based secrets providers.
/// </summary> /// </summary>
[YamlDotNet.Serialization.YamlMember(Alias="encryptedkey")] [YamlMember(Alias="encryptedkey")]
[System.Text.Json.Serialization.JsonPropertyName("encryptedkey")] [JsonPropertyName("encryptedkey")]
public string? EncryptedKey { get; set; } public string? EncryptedKey { get; set; }
@ -27,8 +29,8 @@ namespace Pulumi.Automation
/// This is this stack's base64 encoded encryption salt. Only used for /// This is this stack's base64 encoded encryption salt. Only used for
/// passphrase-based secrets providers. /// passphrase-based secrets providers.
/// </summary> /// </summary>
[YamlDotNet.Serialization.YamlMember(Alias="encryptionsalt")] [YamlMember(Alias="encryptionsalt")]
[System.Text.Json.Serialization.JsonPropertyName("encryptionsalt")] [JsonPropertyName("encryptionsalt")]
public string? EncryptionSalt { get; set; } public string? EncryptionSalt { get; set; }

View file

@ -9,6 +9,8 @@ using System.Threading.Tasks;
using Pulumi.Automation.Commands; using Pulumi.Automation.Commands;
using Pulumi.Automation.Commands.Exceptions; using Pulumi.Automation.Commands.Exceptions;
using Pulumi.Automation.Events; using Pulumi.Automation.Events;
// ReSharper disable UnusedMemberInSuper.Global
// ReSharper disable VirtualMemberNeverOverridden.Global
namespace Pulumi.Automation namespace Pulumi.Automation
{ {
@ -276,7 +278,7 @@ namespace Pulumi.Automation
internal async Task<CommandResult> RunStackCommandAsync( internal async Task<CommandResult> RunStackCommandAsync(
string stackName, string stackName,
IEnumerable<string> args, IList<string> args,
Action<string>? onStandardOutput, Action<string>? onStandardOutput,
Action<string>? onStandardError, Action<string>? onStandardError,
Action<EngineEvent>? onEngineEvent, Action<EngineEvent>? onEngineEvent,
@ -291,12 +293,12 @@ namespace Pulumi.Automation
} }
internal Task<CommandResult> RunCommandAsync( internal Task<CommandResult> RunCommandAsync(
IEnumerable<string> args, IList<string> args,
CancellationToken cancellationToken) CancellationToken cancellationToken)
=> this.RunCommandAsync(args, onStandardOutput: null, onStandardError: null, onEngineEvent: null, cancellationToken); => this.RunCommandAsync(args, onStandardOutput: null, onStandardError: null, onEngineEvent: null, cancellationToken);
internal Task<CommandResult> RunCommandAsync( internal Task<CommandResult> RunCommandAsync(
IEnumerable<string> args, IList<string> args,
Action<string>? onStandardOutput, Action<string>? onStandardOutput,
Action<string>? onStandardError, Action<string>? onStandardError,
Action<EngineEvent>? onEngineEvent, Action<EngineEvent>? onEngineEvent,

View file

@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
@ -117,30 +116,23 @@ namespace Pulumi.Automation
this.Name = name; this.Name = name;
this.Workspace = workspace; this.Workspace = workspace;
switch (mode) this._readyTask = mode switch
{ {
case WorkspaceStackInitMode.Create: WorkspaceStackInitMode.Create => workspace.CreateStackAsync(name, cancellationToken),
this._readyTask = workspace.CreateStackAsync(name, cancellationToken); WorkspaceStackInitMode.Select => workspace.SelectStackAsync(name, cancellationToken),
break; WorkspaceStackInitMode.CreateOrSelect => Task.Run(async () =>
case WorkspaceStackInitMode.Select: {
this._readyTask = workspace.SelectStackAsync(name, cancellationToken); try
break;
case WorkspaceStackInitMode.CreateOrSelect:
this._readyTask = Task.Run(async () =>
{ {
try await workspace.CreateStackAsync(name, cancellationToken).ConfigureAwait(false);
{ }
await workspace.CreateStackAsync(name, cancellationToken).ConfigureAwait(false); catch (StackAlreadyExistsException)
} {
catch (StackAlreadyExistsException) await workspace.SelectStackAsync(name, cancellationToken).ConfigureAwait(false);
{ }
await workspace.SelectStackAsync(name, cancellationToken).ConfigureAwait(false); }),
} _ => throw new InvalidOperationException($"Unexpected Stack creation mode: {mode}")
}); };
break;
default:
throw new InvalidOperationException($"Unexpected Stack creation mode: {mode}");
}
} }
/// <summary> /// <summary>
@ -211,7 +203,7 @@ namespace Pulumi.Automation
{ {
var execKind = ExecKind.Local; var execKind = ExecKind.Local;
var program = this.Workspace.Program; var program = this.Workspace.Program;
var args = new List<string>() var args = new List<string>
{ {
"up", "up",
"--yes", "--yes",
@ -313,7 +305,7 @@ namespace Pulumi.Automation
{ {
var execKind = ExecKind.Local; var execKind = ExecKind.Local;
var program = this.Workspace.Program; var program = this.Workspace.Program;
var args = new List<string>() { "preview" }; var args = new List<string> { "preview" };
if (options != null) if (options != null)
{ {
@ -423,7 +415,7 @@ namespace Pulumi.Automation
RefreshOptions? options = null, RefreshOptions? options = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var args = new List<string>() var args = new List<string>
{ {
"refresh", "refresh",
"--yes", "--yes",
@ -478,7 +470,7 @@ namespace Pulumi.Automation
DestroyOptions? options = null, DestroyOptions? options = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var args = new List<string>() var args = new List<string>
{ {
"destroy", "destroy",
"--yes", "--yes",
@ -539,7 +531,7 @@ namespace Pulumi.Automation
HistoryOptions? options = null, HistoryOptions? options = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
var args = new List<string>() var args = new List<string>
{ {
"stack", "stack",
"history", "history",
@ -616,14 +608,13 @@ namespace Pulumi.Automation
} }
private async Task<CommandResult> RunCommandAsync( private async Task<CommandResult> RunCommandAsync(
IEnumerable<string> args, IList<string> args,
Action<string>? onStandardOutput, Action<string>? onStandardOutput,
Action<string>? onStandardError, Action<string>? onStandardError,
Action<EngineEvent>? onEngineEvent, Action<EngineEvent>? onEngineEvent,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var argsList = args.ToList(); args = args.Concat(new[] { "--stack", this.Name }).ToList();
argsList.AddRange(new List<string>() { "--stack", this.Name });
return await this.Workspace.RunStackCommandAsync(this.Name, args, onStandardOutput, onStandardError, onEngineEvent, cancellationToken); return await this.Workspace.RunStackCommandAsync(this.Name, args, onStandardOutput, onStandardError, onEngineEvent, cancellationToken);
} }

View file

@ -1,11 +1,7 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Pulumi.Serialization;
using Xunit; using Xunit;
namespace Pulumi.Tests.Core namespace Pulumi.Tests.Core
@ -16,14 +12,18 @@ namespace Pulumi.Tests.Core
public Task MergeInputMaps() public Task MergeInputMaps()
=> RunInPreview(async () => => RunInPreview(async () =>
{ {
var map1 = new InputMap<string>(); var map1 = new InputMap<string>
map1.Add("K1", "V1"); {
map1.Add("K2", Output.Create("V2")); { "K1", "V1" },
map1.Add("K3", Output.Create("V3_wrong")); { "K2", Output.Create("V2") },
{ "K3", Output.Create("V3_wrong") }
};
var map2 = new InputMap<string>(); var map2 = new InputMap<string>
map2.Add("K3", Output.Create("V3")); {
map2.Add("K4", "V4"); { "K3", Output.Create("V3") },
{ "K4", "V4" }
};
var result = InputMap<string>.Merge(map1, map2); var result = InputMap<string>.Merge(map1, map2);
@ -31,7 +31,7 @@ namespace Pulumi.Tests.Core
var data = await result.ToOutput().DataTask.ConfigureAwait(false); var data = await result.ToOutput().DataTask.ConfigureAwait(false);
Assert.True(data.IsKnown); Assert.True(data.IsKnown);
Assert.Equal(4, data.Value.Count); Assert.Equal(4, data.Value.Count);
for (int i = 1; i <=4; i++) for (var i = 1; i <=4; i++)
Assert.True(data.Value.Contains($"K{i}", $"V{i}")); Assert.True(data.Value.Contains($"K{i}", $"V{i}"));
// Check that the input maps haven't changed // Check that the input maps haven't changed

View file

@ -8,7 +8,7 @@ using Xunit;
namespace Pulumi.Tests.Core namespace Pulumi.Tests.Core
{ {
public partial class OutputTests : PulumiTest public class OutputTests : PulumiTest
{ {
private static Output<T> CreateOutput<T>(T value, bool isKnown, bool isSecret = false) private static Output<T> CreateOutput<T>(T value, bool isKnown, bool isSecret = false)
=> new Output<T>(Task.FromResult(OutputData.Create( => new Output<T>(Task.FromResult(OutputData.Create(

View file

@ -2,7 +2,6 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using Pulumi.Serialization;
using Xunit; using Xunit;
namespace Pulumi.Tests.Core namespace Pulumi.Tests.Core
@ -17,7 +16,8 @@ namespace Pulumi.Tests.Core
[Input("array")] private InputList<bool> _array = null!; [Input("array")] private InputList<bool> _array = null!;
public InputList<bool> Array public InputList<bool> Array
{ {
get => _array ?? (_array = new InputList<bool>()); // ReSharper disable once ConstantNullCoalescingCondition
get => _array ??= new InputList<bool>();
set => _array = value; set => _array = value;
} }
} }
@ -90,14 +90,16 @@ namespace Pulumi.Tests.Core
[Input("array", json: true)] private InputList<bool> _array = null!; [Input("array", json: true)] private InputList<bool> _array = null!;
public InputList<bool> Array public InputList<bool> Array
{ {
get => _array ?? (_array = new InputList<bool>()); // ReSharper disable once ConstantNullCoalescingCondition
get => _array ??= new InputList<bool>();
set => _array = value; set => _array = value;
} }
[Input("map", json: true)] private InputMap<int> _map = null!; [Input("map", json: true)] private InputMap<int> _map = null!;
public InputMap<int> Map public InputMap<int> Map
{ {
get => _map ?? (_map = new InputMap<int>()); // ReSharper disable once ConstantNullCoalescingCondition
get => _map ??= new InputMap<int>();
set => _map = value; set => _map = value;
} }
} }

View file

@ -17,23 +17,16 @@ namespace Pulumi.Tests.Mocks
return Task.FromResult<object>(args); return Task.FromResult<object>(args);
} }
public Task<(string? id, object state)> NewResourceAsync(MockResourceArgs args) public Task<(string? id, object state)> NewResourceAsync(MockResourceArgs args) =>
{ args.Type switch
switch (args.Type)
{ {
case "aws:ec2/instance:Instance": "aws:ec2/instance:Instance" => Task.FromResult<(string?, object)>(("i-1234567890abcdef0", new Dictionary<string, object> { { "publicIp", "203.0.113.12" }, })),
return Task.FromResult<(string?, object)>(("i-1234567890abcdef0", new Dictionary<string, object> { "pkg:index:MyCustom" => Task.FromResult<(string?, object)>((args.Name + "_id", args.Inputs)),
{ "publicIp", "203.0.113.12" }, _ => throw new Exception($"Unknown resource {args.Type}")
})); };
case "pkg:index:MyCustom":
return Task.FromResult<(string?, object)>((args.Name + "_id", args.Inputs));
default:
throw new Exception($"Unknown resource {args.Type}");
}
}
} }
public partial class MocksTests public class MocksTests
{ {
[Fact] [Fact]
public async Task TestCustom() public async Task TestCustom()
@ -43,7 +36,7 @@ namespace Pulumi.Tests.Mocks
var instance = resources.OfType<Instance>().FirstOrDefault(); var instance = resources.OfType<Instance>().FirstOrDefault();
Assert.NotNull(instance); Assert.NotNull(instance);
var ip = await instance.PublicIp.GetValueAsync(); var ip = await instance!.PublicIp.GetValueAsync();
Assert.Equal("203.0.113.12", ip); Assert.Equal("203.0.113.12", ip);
} }
@ -52,10 +45,10 @@ namespace Pulumi.Tests.Mocks
{ {
var resources = await Testing.RunAsync<MyStack>(); var resources = await Testing.RunAsync<MyStack>();
var mycustom = resources.OfType<MyCustom>().FirstOrDefault(); var myCustom = resources.OfType<MyCustom>().FirstOrDefault();
Assert.NotNull(mycustom); Assert.NotNull(myCustom);
var instance = await mycustom.Instance.GetValueAsync(); var instance = await myCustom!.Instance.GetValueAsync();
Assert.IsType<Instance>(instance); Assert.IsType<Instance>(instance);
var ip = await instance.PublicIp.GetValueAsync(); var ip = await instance.PublicIp.GetValueAsync();
@ -70,7 +63,7 @@ namespace Pulumi.Tests.Mocks
var stack = resources.OfType<MyStack>().FirstOrDefault(); var stack = resources.OfType<MyStack>().FirstOrDefault();
Assert.NotNull(stack); Assert.NotNull(stack);
var ip = await stack.PublicIp.GetValueAsync(); var ip = await stack!.PublicIp.GetValueAsync();
Assert.Equal("203.0.113.12", ip); Assert.Equal("203.0.113.12", ip);
} }
} }

View file

@ -3,33 +3,33 @@
namespace Pulumi.Tests.Mocks namespace Pulumi.Tests.Mocks
{ {
[ResourceType("aws:ec2/instance:Instance", null)] [ResourceType("aws:ec2/instance:Instance", null)]
public partial class Instance : Pulumi.CustomResource public class Instance : CustomResource
{ {
[Output("publicIp")] [Output("publicIp")]
public Output<string> PublicIp { get; private set; } = null!; public Output<string> PublicIp { get; private set; } = null!;
public Instance(string name, InstanceArgs args, CustomResourceOptions? options = null) public Instance(string name, InstanceArgs args, CustomResourceOptions? options = null)
: base("aws:ec2/instance:Instance", name, args ?? new InstanceArgs(), options) : base("aws:ec2/instance:Instance", name, args, options)
{ {
} }
} }
public sealed class InstanceArgs : Pulumi.ResourceArgs public sealed class InstanceArgs : ResourceArgs
{ {
} }
public partial class MyCustom : Pulumi.CustomResource public class MyCustom : CustomResource
{ {
[Output("instance")] [Output("instance")]
public Output<Instance> Instance { get; private set; } = null!; public Output<Instance> Instance { get; private set; } = null!;
public MyCustom(string name, MyCustomArgs args, CustomResourceOptions? options = null) public MyCustom(string name, MyCustomArgs args, CustomResourceOptions? options = null)
: base("pkg:index:MyCustom", name, args ?? new MyCustomArgs(), options) : base("pkg:index:MyCustom", name, args, options)
{ {
} }
} }
public sealed class MyCustomArgs : Pulumi.ResourceArgs public sealed class MyCustomArgs : ResourceArgs
{ {
[Input("instance")] [Input("instance")]
public Input<Instance>? Instance { get; set; } public Input<Instance>? Instance { get; set; }
@ -38,15 +38,12 @@ namespace Pulumi.Tests.Mocks
public class MyStack : Stack public class MyStack : Stack
{ {
[Output("publicIp")] [Output("publicIp")]
public Output<string> PublicIp { get; private set; } = null!; public Output<string> PublicIp { get; private set; }
public MyStack() public MyStack()
{ {
var myInstance = new Instance("instance", new InstanceArgs()); var myInstance = new Instance("instance", new InstanceArgs());
var myCustom = new MyCustom("mycustom", new MyCustomArgs new MyCustom("mycustom", new MyCustomArgs { Instance = myInstance });
{
Instance = myInstance,
});
this.PublicIp = myInstance.PublicIp; this.PublicIp = myInstance.PublicIp;
} }
} }

View file

@ -52,7 +52,7 @@ namespace Pulumi.Tests.Serialization
{ {
Assert.Throws<InvalidOperationException>(() => Assert.Throws<InvalidOperationException>(() =>
{ {
var data = Converter.ConvertValue<bool>("", new Value { StringValue = "" }); Converter.ConvertValue<bool>("", new Value { StringValue = "" });
}); });
} }
@ -94,7 +94,7 @@ namespace Pulumi.Tests.Serialization
{ {
Assert.Throws<InvalidOperationException>(() => Assert.Throws<InvalidOperationException>(() =>
{ {
var data = Converter.ConvertValue<bool>("", new Value { StringValue = "" }); Converter.ConvertValue<bool>("", new Value { StringValue = "" });
}); });
} }

View file

@ -35,7 +35,7 @@ namespace Pulumi.Tests.Serialization
public override bool Equals(object? obj) => obj is ContainerColor other && Equals(other); public override bool Equals(object? obj) => obj is ContainerColor other && Equals(other);
public bool Equals(ContainerColor other) => string.Equals(_value, other._value, StringComparison.Ordinal); public bool Equals(ContainerColor other) => string.Equals(_value, other._value, StringComparison.Ordinal);
public override int GetHashCode() => _value?.GetHashCode() ?? 0; public override int GetHashCode() => _value.GetHashCode();
public override string ToString() => _value; public override string ToString() => _value;
} }

View file

@ -3,9 +3,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization;
using System.Threading.Tasks; using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes;
using Pulumi.Serialization; using Pulumi.Serialization;
using Xunit; using Xunit;
using Type = System.Type;
namespace Pulumi.Tests.Serialization namespace Pulumi.Tests.Serialization
{ {
@ -35,7 +38,7 @@ namespace Pulumi.Tests.Serialization
public bool Equals(ContainerColor other) => string.Equals(_value, other._value, StringComparison.Ordinal); public bool Equals(ContainerColor other) => string.Equals(_value, other._value, StringComparison.Ordinal);
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => _value?.GetHashCode() ?? 0; public override int GetHashCode() => _value.GetHashCode();
public override string ToString() => _value; public override string ToString() => _value;
} }
@ -60,12 +63,13 @@ namespace Pulumi.Tests.Serialization
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object? obj) => obj is ContainerBrightness other && Equals(other); public override bool Equals(object? obj) => obj is ContainerBrightness other && Equals(other);
// ReSharper disable once CompareOfFloatsByEqualityOperator
public bool Equals(ContainerBrightness other) => _value == other._value; public bool Equals(ContainerBrightness other) => _value == other._value;
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() => _value.GetHashCode(); public override int GetHashCode() => _value.GetHashCode();
public override string ToString() => _value.ToString(); public override string ToString() => _value.ToString(CultureInfo.InvariantCulture);
} }
public enum ContainerSize public enum ContainerSize
@ -196,14 +200,14 @@ namespace Pulumi.Tests.Serialization
public static IEnumerable<object[]> EnumsWithUnconvertibleValues() public static IEnumerable<object[]> EnumsWithUnconvertibleValues()
=> new[] => new[]
{ {
new object[] { typeof(ContainerColor), new Google.Protobuf.WellKnownTypes.Value { NumberValue = 1.0 } }, new object[] { typeof(ContainerColor), new Value { NumberValue = 1.0 } },
new object[] { typeof(ContainerBrightness), new Google.Protobuf.WellKnownTypes.Value { StringValue = "hello" } }, new object[] { typeof(ContainerBrightness), new Value { StringValue = "hello" } },
new object[] { typeof(ContainerSize), new Google.Protobuf.WellKnownTypes.Value { StringValue = "hello" } }, new object[] { typeof(ContainerSize), new Value { StringValue = "hello" } },
}; };
[Theory] [Theory]
[MemberData(nameof(EnumsWithUnconvertibleValues))] [MemberData(nameof(EnumsWithUnconvertibleValues))]
public void ConvertingUnconvertibleValuesThrows(Type targetType, Google.Protobuf.WellKnownTypes.Value value) public void ConvertingUnconvertibleValuesThrows(Type targetType, Value value)
{ {
Assert.Throws<InvalidOperationException>(() => Assert.Throws<InvalidOperationException>(() =>
{ {

View file

@ -1,9 +1,6 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes; using Google.Protobuf.WellKnownTypes;
using Pulumi.Serialization; using Pulumi.Serialization;
using Xunit; using Xunit;

View file

@ -14,7 +14,7 @@ namespace Pulumi.Tests.Serialization
{ {
public class ResourceRefPropertyTests : ConverterTests public class ResourceRefPropertyTests : ConverterTests
{ {
public sealed class MyArgs : Pulumi.ResourceArgs public sealed class MyArgs : ResourceArgs
{ {
} }
@ -52,18 +52,18 @@ namespace Pulumi.Tests.Serialization
{ {
public MyStack() public MyStack()
{ {
var customResource = new MyCustomResource("test", null, null); new MyCustomResource("test", null);
var componentResource = new MyComponentResource("test", null, null); new MyComponentResource("test", null);
} }
} }
class MyMocks : IMocks class MyMocks : IMocks
{ {
bool isPreview; private readonly bool _isPreview;
public MyMocks(bool isPreview) public MyMocks(bool isPreview)
{ {
this.isPreview = isPreview; this._isPreview = isPreview;
} }
public Task<object> CallAsync(MockCallArgs args) public Task<object> CallAsync(MockCallArgs args)
@ -77,10 +77,10 @@ namespace Pulumi.Tests.Serialization
{ {
case "test:index:resource": case "test:index:resource":
case "test:missing:resource": case "test:missing:resource":
return Task.FromResult<(string?, object)>((this.isPreview ? null : "id", new Dictionary<string, object> {})); return Task.FromResult<(string?, object)>((this._isPreview ? null : "id", new Dictionary<string, object>()));
case "test:index:component": case "test:index:component":
case "test:missing:component": case "test:missing:component":
return Task.FromResult<(string?, object)>((null, new Dictionary<string, object> {})); return Task.FromResult<(string?, object)>((null, new Dictionary<string, object>()));
default: default:
throw new Exception($"Unknown resource {args.Type}"); throw new Exception($"Unknown resource {args.Type}");
} }
@ -129,7 +129,7 @@ namespace Pulumi.Tests.Serialization
var res = resources.OfType<MyCustomResource>().FirstOrDefault(); var res = resources.OfType<MyCustomResource>().FirstOrDefault();
Assert.NotNull(res); Assert.NotNull(res);
var urn = (await res.Urn.DataTask).Value; var urn = (await res!.Urn.DataTask).Value;
var id = (await res.Id.DataTask).Value; var id = (await res.Id.DataTask).Value;
var v = await SerializeToValueAsync(res); var v = await SerializeToValueAsync(res);
@ -145,7 +145,7 @@ namespace Pulumi.Tests.Serialization
var res = resources.OfType<MyCustomResource>().FirstOrDefault(); var res = resources.OfType<MyCustomResource>().FirstOrDefault();
Assert.NotNull(res); Assert.NotNull(res);
var id = await SerializeToValueAsync(res.Id); var id = await SerializeToValueAsync(res!.Id);
var v = await SerializeToValueAsync(res, false); var v = await SerializeToValueAsync(res, false);
Assert.Equal(id, v); Assert.Equal(id, v);
@ -154,11 +154,11 @@ namespace Pulumi.Tests.Serialization
public class DeserializeCustomResourceStack : Stack public class DeserializeCustomResourceStack : Stack
{ {
[Output("values")] [Output("values")]
public Output<ImmutableDictionary<string, string>> Values { get; private set; } = null!; public Output<ImmutableDictionary<string, string>> Values { get; private set; }
public DeserializeCustomResourceStack() public DeserializeCustomResourceStack()
{ {
var res = new MyCustomResource("test", null, null); var res = new MyCustomResource("test", null);
var urn = res.Urn.DataTask.Result.Value; var urn = res.Urn.DataTask.Result.Value;
var id = res.Id.DataTask.Result.Value; var id = res.Id.DataTask.Result.Value;
@ -184,7 +184,7 @@ namespace Pulumi.Tests.Serialization
var stack = resources.OfType<DeserializeCustomResourceStack>().FirstOrDefault(); var stack = resources.OfType<DeserializeCustomResourceStack>().FirstOrDefault();
Assert.NotNull(stack); Assert.NotNull(stack);
var values = (await stack.Values.DataTask).Value; var values = (await stack!.Values.DataTask).Value;
Assert.Equal(values["expectedUrn"], values["actualUrn"]); Assert.Equal(values["expectedUrn"], values["actualUrn"]);
Assert.Equal(values["expectedId"], values["actualId"]); Assert.Equal(values["expectedId"], values["actualId"]);
} }
@ -192,11 +192,11 @@ namespace Pulumi.Tests.Serialization
public class DeserializeMissingCustomResourceStack : Stack public class DeserializeMissingCustomResourceStack : Stack
{ {
[Output("values")] [Output("values")]
public Output<ImmutableDictionary<string, string>> Values { get; private set; } = null!; public Output<ImmutableDictionary<string, string>> Values { get; private set; }
public DeserializeMissingCustomResourceStack() public DeserializeMissingCustomResourceStack()
{ {
var res = new MissingCustomResource("test", null, null); var res = new MissingCustomResource("test", null);
var urn = res.Urn.DataTask.Result.Value; var urn = res.Urn.DataTask.Result.Value;
@ -219,7 +219,7 @@ namespace Pulumi.Tests.Serialization
var stack = resources.OfType<DeserializeMissingCustomResourceStack>().FirstOrDefault(); var stack = resources.OfType<DeserializeMissingCustomResourceStack>().FirstOrDefault();
Assert.NotNull(stack); Assert.NotNull(stack);
var values = (await stack.Values.DataTask).Value; var values = (await stack!.Values.DataTask).Value;
Assert.Equal(values["expectedUrn"], values["actualUrn"]); Assert.Equal(values["expectedUrn"], values["actualUrn"]);
} }
@ -232,7 +232,7 @@ namespace Pulumi.Tests.Serialization
var res = resources.OfType<MyComponentResource>().FirstOrDefault(); var res = resources.OfType<MyComponentResource>().FirstOrDefault();
Assert.NotNull(res); Assert.NotNull(res);
var urn = (await res.Urn.DataTask).Value; var urn = (await res!.Urn.DataTask).Value;
var v = await SerializeToValueAsync(res); var v = await SerializeToValueAsync(res);
Assert.Equal(CreateComponentResourceReference(urn), v); Assert.Equal(CreateComponentResourceReference(urn), v);
@ -247,7 +247,7 @@ namespace Pulumi.Tests.Serialization
var res = resources.OfType<MyComponentResource>().FirstOrDefault(); var res = resources.OfType<MyComponentResource>().FirstOrDefault();
Assert.NotNull(res); Assert.NotNull(res);
var urn = await SerializeToValueAsync(res.Urn); var urn = await SerializeToValueAsync(res!.Urn);
var v = await SerializeToValueAsync(res, false); var v = await SerializeToValueAsync(res, false);
Assert.Equal(urn, v); Assert.Equal(urn, v);
@ -256,11 +256,11 @@ namespace Pulumi.Tests.Serialization
public class DeserializeComponentResourceStack : Stack public class DeserializeComponentResourceStack : Stack
{ {
[Output("values")] [Output("values")]
public Output<ImmutableDictionary<string, string>> Values { get; private set; } = null!; public Output<ImmutableDictionary<string, string>> Values { get; private set; }
public DeserializeComponentResourceStack() public DeserializeComponentResourceStack()
{ {
var res = new MyComponentResource("test", null, null); var res = new MyComponentResource("test", null);
var urn = res.Urn.DataTask.Result.Value; var urn = res.Urn.DataTask.Result.Value;
@ -283,18 +283,18 @@ namespace Pulumi.Tests.Serialization
var stack = resources.OfType<DeserializeComponentResourceStack>().FirstOrDefault(); var stack = resources.OfType<DeserializeComponentResourceStack>().FirstOrDefault();
Assert.NotNull(stack); Assert.NotNull(stack);
var values = (await stack.Values.DataTask).Value; var values = (await stack!.Values.DataTask).Value;
Assert.Equal(values["expectedUrn"], values["actualUrn"]); Assert.Equal(values["expectedUrn"], values["actualUrn"]);
} }
public class DeserializeMissingComponentResourceStack : Stack public class DeserializeMissingComponentResourceStack : Stack
{ {
[Output("values")] [Output("values")]
public Output<ImmutableDictionary<string, string>> Values { get; private set; } = null!; public Output<ImmutableDictionary<string, string>> Values { get; private set; }
public DeserializeMissingComponentResourceStack() public DeserializeMissingComponentResourceStack()
{ {
var res = new MissingComponentResource("test", null, null); var res = new MissingComponentResource("test", null);
var urn = res.Urn.DataTask.Result.Value; var urn = res.Urn.DataTask.Result.Value;
@ -317,7 +317,7 @@ namespace Pulumi.Tests.Serialization
var stack = resources.OfType<DeserializeMissingComponentResourceStack>().FirstOrDefault(); var stack = resources.OfType<DeserializeMissingComponentResourceStack>().FirstOrDefault();
Assert.NotNull(stack); Assert.NotNull(stack);
var values = (await stack.Values.DataTask).Value; var values = (await stack!.Values.DataTask).Value;
Assert.Equal(values["expectedUrn"], values["actualUrn"]); Assert.Equal(values["expectedUrn"], values["actualUrn"]);
} }
} }

View file

@ -50,7 +50,7 @@ namespace Pulumi.Tests.Serialization
{ {
Assert.Throws<InvalidOperationException>(() => Assert.Throws<InvalidOperationException>(() =>
{ {
var data = Converter.ConvertValue<Union<int, string>>("", new Value { BoolValue = true }); Converter.ConvertValue<Union<int, string>>("", new Value { BoolValue = true });
}); });
} }
} }

View file

@ -38,7 +38,7 @@ namespace Pulumi.Tests
private class NullOutputStack : Stack private class NullOutputStack : Stack
{ {
[Output("foo")] [Output("foo")]
public Output<string>? Foo { get; } public Output<string>? Foo { get; } = null;
} }
[Fact] [Fact]
@ -46,7 +46,7 @@ namespace Pulumi.Tests
{ {
try try
{ {
var (stack, outputs) = await Run<NullOutputStack>(); await Run<NullOutputStack>();
} }
catch (RunException ex) catch (RunException ex)
{ {
@ -73,7 +73,7 @@ namespace Pulumi.Tests
{ {
try try
{ {
var (stack, outputs) = await Run<InvalidOutputTypeStack>(); await Run<InvalidOutputTypeStack>();
} }
catch (RunException ex) catch (RunException ex)
{ {

View file

@ -1,7 +1,6 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
using System; using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.Json; using System.Text.Json;
@ -32,14 +31,10 @@ namespace Pulumi
/// </summary> /// </summary>
public Config(string? name = null) public Config(string? name = null)
{ {
if (name == null) name ??= Deployment.Instance.ProjectName;
{
name = Deployment.Instance.ProjectName;
}
if (name.EndsWith(":config", StringComparison.Ordinal)) if (name.EndsWith(":config", StringComparison.Ordinal))
{ {
name = name[0..^":config".Length]; name = name[..^":config".Length];
} }
_name = name; _name = name;
@ -84,9 +79,13 @@ namespace Pulumi
private bool? GetBooleanImpl(string key, string? use = null, [CallerMemberName] string? insteadOf = null) private bool? GetBooleanImpl(string key, string? use = null, [CallerMemberName] string? insteadOf = null)
{ {
var v = GetImpl(key, use, insteadOf); var v = GetImpl(key, use, insteadOf);
return v == null ? default(bool?) : return v switch
v == "true" ? true : {
v == "false" ? false : throw new ConfigTypeException(FullKey(key), v, nameof(Boolean)); null => default(bool?),
"true" => true,
"false" => false,
_ => throw new ConfigTypeException(FullKey(key), v, nameof(Boolean))
};
} }
/// <summary> /// <summary>
@ -155,8 +154,7 @@ namespace Pulumi
/// <summary> /// <summary>
/// Loads an optional configuration value, as an object, by its key, marking it as a secret /// Loads an optional configuration value, as an object, by its key, marking it as a secret
/// or null if it doesn't exist. This works by taking the value associated with <paramref /// or null if it doesn't exist. This works by taking the value associated with <paramref
/// name="key"/> and passing it to <see cref="JsonSerializer.Deserialize{TValue}(string, /// name="key"/> and passing it to <see cref="JsonSerializer.Deserialize{TValue}(string, JsonSerializerOptions)"/>.
/// JsonSerializerOptions)"/>.
/// </summary> /// </summary>
public Output<T>? GetSecretObject<T>(string key) public Output<T>? GetSecretObject<T>(string key)
{ {
@ -238,8 +236,8 @@ namespace Pulumi
/// <summary> /// <summary>
/// Loads a configuration value as a JSON string and deserializes the JSON into a JavaScript /// Loads a configuration value as a JSON string and deserializes the JSON into a JavaScript
/// object, marking it as a secret. If it doesn't exist, or the configuration value cannot /// object, marking it as a secret. If it doesn't exist, or the configuration value cannot
/// be converted using <see cref="JsonSerializer.Deserialize{TValue}(string, /// be converted using <see cref="JsonSerializer.Deserialize{TValue}(string, JsonSerializerOptions)"/>,
/// JsonSerializerOptions)"/>. an error is thrown. /// an error is thrown.
/// </summary> /// </summary>
public Output<T> RequireSecretObject<T>(string key) public Output<T> RequireSecretObject<T>(string key)
=> Output.CreateSecret(RequireObjectImpl<T>(key)); => Output.CreateSecret(RequireObjectImpl<T>(key));

View file

@ -11,12 +11,7 @@ namespace Pulumi
/// </summary> /// </summary>
private class ConfigTypeException : RunException private class ConfigTypeException : RunException
{ {
public ConfigTypeException(string key, object? v, string expectedType) public ConfigTypeException(string key, object? v, string expectedType, Exception? innerException = null)
: this(key, v, expectedType, innerException: null)
{
}
public ConfigTypeException(string key, object? v, string expectedType, Exception? innerException)
: base($"Configuration '{key}' value '{v}' is not a valid {expectedType}", innerException) : base($"Configuration '{key}' value '{v}' is not a valid {expectedType}", innerException)
{ {
} }

View file

@ -1,5 +1,7 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
using System;
namespace Pulumi namespace Pulumi
{ {
/// <summary> /// <summary>
@ -13,9 +15,9 @@ namespace Pulumi
private protected AssetOrArchive(string sigKey, string propName, object value) private protected AssetOrArchive(string sigKey, string propName, object value)
{ {
SigKey = sigKey ?? throw new System.ArgumentNullException(nameof(sigKey)); SigKey = sigKey ?? throw new ArgumentNullException(nameof(sigKey));
PropName = propName ?? throw new System.ArgumentNullException(nameof(propName)); PropName = propName ?? throw new ArgumentNullException(nameof(propName));
Value = value ?? throw new System.ArgumentNullException(nameof(value)); Value = value ?? throw new ArgumentNullException(nameof(value));
} }
} }
} }

View file

@ -127,13 +127,13 @@ namespace Pulumi
#region construct from Output of some list type. #region construct from Output of some list type.
public static implicit operator InputList<T>(Output<T[]> values) public static implicit operator InputList<T>(Output<T[]> values)
=> values.Apply(a => ImmutableArray.CreateRange(a)); => values.Apply(ImmutableArray.CreateRange);
public static implicit operator InputList<T>(Output<List<T>> values) public static implicit operator InputList<T>(Output<List<T>> values)
=> values.Apply(a => ImmutableArray.CreateRange(a)); => values.Apply(ImmutableArray.CreateRange);
public static implicit operator InputList<T>(Output<IEnumerable<T>> values) public static implicit operator InputList<T>(Output<IEnumerable<T>> values)
=> values.Apply(a => ImmutableArray.CreateRange(a)); => values.Apply(ImmutableArray.CreateRange);
public static implicit operator InputList<T>(Output<ImmutableArray<T>> values) public static implicit operator InputList<T>(Output<ImmutableArray<T>> values)
=> new InputList<T>(values); => new InputList<T>(values);

View file

@ -13,7 +13,7 @@ namespace Pulumi
/// <see cref="Resource"/>. The individual values are themselves <see cref="Input{T}"/>s. i.e. /// <see cref="Resource"/>. The individual values are themselves <see cref="Input{T}"/>s. i.e.
/// the individual values can be concrete values or <see cref="Output{T}"/>s. /// the individual values can be concrete values or <see cref="Output{T}"/>s.
/// <para/> /// <para/>
/// <see cref="InputMap{V}"/> differs from a normal <see cref="IDictionary{K,V}"/> in that it is /// <see cref="InputMap{V}"/> differs from a normal <see cref="IDictionary{TKey,TValue}"/> in that it is
/// itself an <see cref="Input{T}"/>. For example, a <see cref="Resource"/> that accepts an /// itself an <see cref="Input{T}"/>. For example, a <see cref="Resource"/> that accepts an
/// <see cref="InputMap{V}"/> will accept not just a dictionary but an <see cref="Output{T}"/> /// <see cref="InputMap{V}"/> will accept not just a dictionary but an <see cref="Output{T}"/>
/// of a dictionary as well. This is important for cases where the <see cref="Output{T}"/> /// of a dictionary as well. This is important for cases where the <see cref="Output{T}"/>
@ -96,10 +96,10 @@ namespace Pulumi
=> Output.Create(values); => Output.Create(values);
public static implicit operator InputMap<V>(Output<Dictionary<string, V>> values) public static implicit operator InputMap<V>(Output<Dictionary<string, V>> values)
=> values.Apply(d => ImmutableDictionary.CreateRange(d)); => values.Apply(ImmutableDictionary.CreateRange);
public static implicit operator InputMap<V>(Output<IDictionary<string, V>> values) public static implicit operator InputMap<V>(Output<IDictionary<string, V>> values)
=> values.Apply(d => ImmutableDictionary.CreateRange(d)); => values.Apply(ImmutableDictionary.CreateRange);
public static implicit operator InputMap<V>(Output<ImmutableDictionary<string, V>> values) public static implicit operator InputMap<V>(Output<ImmutableDictionary<string, V>> values)
=> new InputMap<V>(values); => new InputMap<V>(values);

View file

@ -1,7 +1,5 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
using System;
namespace Pulumi namespace Pulumi
{ {
/// <summary> /// <summary>
@ -29,10 +27,10 @@ namespace Pulumi
=> Output.Create(value); => Output.Create(value);
public static implicit operator InputUnion<T0, T1>(Output<T0> value) public static implicit operator InputUnion<T0, T1>(Output<T0> value)
=> new InputUnion<T0, T1>(value.Apply(v => Union<T0, T1>.FromT0(v))); => new InputUnion<T0, T1>(value.Apply(Union<T0, T1>.FromT0));
public static implicit operator InputUnion<T0, T1>(Output<T1> value) public static implicit operator InputUnion<T0, T1>(Output<T1> value)
=> new InputUnion<T0, T1>(value.Apply(v => Union<T0, T1>.FromT1(v))); => new InputUnion<T0, T1>(value.Apply(Union<T0, T1>.FromT1));
#endregion #endregion
} }

View file

@ -1,5 +1,6 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
// ReSharper disable NotAccessedField.Global
namespace Pulumi namespace Pulumi
{ {
internal class Options internal class Options

View file

@ -192,19 +192,19 @@ namespace Pulumi
} }
/// <summary> /// <summary>
/// <see cref="Apply{U}(Func{T, Output{U}})"/> for more details. /// <see cref="Output{T}.Apply{U}(Func{T, Output{U}})"/> for more details.
/// </summary> /// </summary>
public Output<U> Apply<U>(Func<T, U> func) public Output<U> Apply<U>(Func<T, U> func)
=> Apply(t => Output.Create(func(t))); => Apply(t => Output.Create(func(t)));
/// <summary> /// <summary>
/// <see cref="Apply{U}(Func{T, Output{U}})"/> for more details. /// <see cref="Output{T}.Apply{U}(Func{T, Output{U}})"/> for more details.
/// </summary> /// </summary>
public Output<U> Apply<U>(Func<T, Task<U>> func) public Output<U> Apply<U>(Func<T, Task<U>> func)
=> Apply(t => Output.Create(func(t))); => Apply(t => Output.Create(func(t)));
/// <summary> /// <summary>
/// <see cref="Apply{U}(Func{T, Output{U}})"/> for more details. /// <see cref="Output{T}.Apply{U}(Func{T, Output{U}})"/> for more details.
/// </summary> /// </summary>
public Output<U> Apply<U>(Func<T, Input<U>?> func) public Output<U> Apply<U>(Func<T, Input<U>?> func)
=> Apply(t => func(t).ToOutput()); => Apply(t => func(t).ToOutput());
@ -303,6 +303,7 @@ namespace Pulumi
var isKnown = true; var isKnown = true;
var isSecret = false; var isSecret = false;
#pragma warning disable 8601
Update(await GetData(item1).ConfigureAwait(false), ref tuple.Item1); Update(await GetData(item1).ConfigureAwait(false), ref tuple.Item1);
Update(await GetData(item2).ConfigureAwait(false), ref tuple.Item2); Update(await GetData(item2).ConfigureAwait(false), ref tuple.Item2);
Update(await GetData(item3).ConfigureAwait(false), ref tuple.Item3); Update(await GetData(item3).ConfigureAwait(false), ref tuple.Item3);
@ -311,6 +312,7 @@ namespace Pulumi
Update(await GetData(item6).ConfigureAwait(false), ref tuple.Item6); Update(await GetData(item6).ConfigureAwait(false), ref tuple.Item6);
Update(await GetData(item7).ConfigureAwait(false), ref tuple.Item7); Update(await GetData(item7).ConfigureAwait(false), ref tuple.Item7);
Update(await GetData(item8).ConfigureAwait(false), ref tuple.Item8); Update(await GetData(item8).ConfigureAwait(false), ref tuple.Item8);
#pragma warning restore 8601
return OutputData.Create(resources.ToImmutable(), tuple, isKnown, isSecret); return OutputData.Create(resources.ToImmutable(), tuple, isKnown, isSecret);

View file

@ -3,6 +3,8 @@
using System; using System;
using OneOf; using OneOf;
// ReSharper disable PossiblyImpureMethodCallOnReadonlyVariable
namespace Pulumi namespace Pulumi
{ {
/// <summary> /// <summary>
@ -26,7 +28,7 @@ namespace Pulumi
/// or a <see cref="string"/> can be represented as <c>Output&lt;int, string&gt;</c>. The <see /// or a <see cref="string"/> can be represented as <c>Output&lt;int, string&gt;</c>. The <see
/// cref="Input{T}"/> version of this is <see cref="InputUnion{T0, T1}"/>. /// cref="Input{T}"/> version of this is <see cref="InputUnion{T0, T1}"/>.
/// </summary> /// </summary>
public struct Union<T0, T1> : IEquatable<Union<T0, T1>>, IUnion public readonly struct Union<T0, T1> : IEquatable<Union<T0, T1>>, IUnion
{ {
private readonly OneOf<T0, T1> _data; private readonly OneOf<T0, T1> _data;

View file

@ -66,10 +66,7 @@ namespace Pulumi
if (childName!.StartsWith(parentName, StringComparison.Ordinal)) if (childName!.StartsWith(parentName, StringComparison.Ordinal))
{ {
aliasName = parentAlias.ToOutput().Apply<string>(parentAliasUrn => aliasName = parentAlias.ToOutput().Apply<string>(parentAliasUrn =>
{ parentAliasUrn.Substring(parentAliasUrn.LastIndexOf("::", StringComparison.Ordinal) + 2) + childName.Substring(parentName.Length));
return parentAliasUrn.Substring(parentAliasUrn.LastIndexOf("::", StringComparison.Ordinal) + 2)
+ childName.Substring(parentName.Length);
});
} }
var urn = Create( var urn = Create(

View file

@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -29,7 +30,7 @@ namespace Pulumi
public Runner(IDeploymentInternal deployment) public Runner(IDeploymentInternal deployment)
=> _deployment = deployment; => _deployment = deployment;
public Task<int> RunAsync<TStack>(IServiceProvider serviceProvider) where TStack : Stack Task<int> IRunner.RunAsync<TStack>(IServiceProvider serviceProvider)
{ {
if (serviceProvider == null) if (serviceProvider == null)
{ {
@ -40,8 +41,7 @@ namespace Pulumi
?? throw new ApplicationException($"Failed to resolve instance of type {typeof(TStack)} from service provider. Register the type with the service provider before calling {nameof(RunAsync)}.")); ?? throw new ApplicationException($"Failed to resolve instance of type {typeof(TStack)} from service provider. Register the type with the service provider before calling {nameof(RunAsync)}."));
} }
public Task<int> RunAsync<TStack>() where TStack : Stack, new() Task<int> IRunner.RunAsync<TStack>() => RunAsync(() => new TStack());
=> RunAsync(() => new TStack());
public Task<int> RunAsync<TStack>(Func<TStack> stackFactory) where TStack : Stack public Task<int> RunAsync<TStack>(Func<TStack> stackFactory) where TStack : Stack
{ {
@ -60,7 +60,7 @@ namespace Pulumi
return WhileRunningAsync(); return WhileRunningAsync();
} }
public Task<int> RunAsync(Func<Task<IDictionary<string, object?>>> func, StackOptions? options) Task<int> IRunner.RunAsync(Func<Task<IDictionary<string, object?>>> func, StackOptions? options)
{ {
var stack = new Stack(func, options); var stack = new Stack(func, options);
RegisterTask("User program code.", stack.Outputs.DataTask); RegisterTask("User program code.", stack.Outputs.DataTask);
@ -214,10 +214,9 @@ namespace Pulumi
} }
else else
{ {
var location = System.Reflection.Assembly.GetEntryAssembly()?.Location; var location = Assembly.GetEntryAssembly()?.Location;
await _deployment.Logger.ErrorAsync( await _deployment.Logger.ErrorAsync($@"Running program '{location}' failed with an unhandled exception:
$@"Running program '{location}' failed with an unhandled exception: {exception}").ConfigureAwait(false);
{exception.ToString()}").ConfigureAwait(false);
} }
_deployment.Serilogger.Debug("Wrote last error. Returning from program."); _deployment.Serilogger.Debug("Wrote last error. Returning from program.");

View file

@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Pulumi.Testing; using Pulumi.Testing;
using Pulumirpc;
using Serilog; using Serilog;
using Serilog.Events; using Serilog.Events;
@ -30,7 +31,7 @@ namespace Pulumi
/// </para> /// </para>
/// Importantly: Cloud resources cannot be created outside of the lambda passed to any of the /// Importantly: Cloud resources cannot be created outside of the lambda passed to any of the
/// <see cref="Deployment.RunAsync(Action)"/> overloads. Because cloud Resource construction is /// <see cref="Deployment.RunAsync(Action)"/> overloads. Because cloud Resource construction is
/// inherently asynchronous, the result of this function is a <see cref="Task{T}"/> which should /// inherently asynchronous, the result of this function is a <see cref="Task{TResult}"/> which should
/// then be returned or awaited. This will ensure that any problems that are encountered during /// then be returned or awaited. This will ensure that any problems that are encountered during
/// the running of the program are properly reported. Failure to do this may lead to the /// the running of the program are properly reported. Failure to do this may lead to the
/// program ending early before all resources are properly registered. /// program ending early before all resources are properly registered.
@ -38,7 +39,7 @@ namespace Pulumi
public sealed partial class Deployment : IDeploymentInternal public sealed partial class Deployment : IDeploymentInternal
{ {
private static readonly object _instanceLock = new object(); private static readonly object _instanceLock = new object();
private static AsyncLocal<DeploymentInstance?> _instance = new AsyncLocal<DeploymentInstance?>(); private static readonly AsyncLocal<DeploymentInstance?> _instance = new AsyncLocal<DeploymentInstance?>();
/// <summary> /// <summary>
/// The current running deployment instance. This is only available from inside the function /// The current running deployment instance. This is only available from inside the function
@ -101,6 +102,7 @@ namespace Pulumi
private Deployment() private Deployment()
{ {
// ReSharper disable UnusedVariable
var monitor = Environment.GetEnvironmentVariable("PULUMI_MONITOR"); var monitor = Environment.GetEnvironmentVariable("PULUMI_MONITOR");
var engine = Environment.GetEnvironmentVariable("PULUMI_ENGINE"); var engine = Environment.GetEnvironmentVariable("PULUMI_ENGINE");
var project = Environment.GetEnvironmentVariable("PULUMI_PROJECT"); var project = Environment.GetEnvironmentVariable("PULUMI_PROJECT");
@ -121,6 +123,7 @@ namespace Pulumi
{ {
throw new InvalidOperationException("Program run without the Pulumi engine available; re-run using the `pulumi` CLI"); throw new InvalidOperationException("Program run without the Pulumi engine available; re-run using the `pulumi` CLI");
} }
// ReSharper restore UnusedVariable
_isDryRun = dryRunValue; _isDryRun = dryRunValue;
_stackName = stack; _stackName = stack;
@ -193,7 +196,7 @@ namespace Pulumi
{ {
if (!this._featureSupport.ContainsKey(feature)) if (!this._featureSupport.ContainsKey(feature))
{ {
var request = new Pulumirpc.SupportsFeatureRequest {Id = feature }; var request = new SupportsFeatureRequest {Id = feature };
var response = await this.Monitor.SupportsFeatureAsync(request).ConfigureAwait(false); var response = await this.Monitor.SupportsFeatureAsync(request).ConfigureAwait(false);
this._featureSupport[feature] = response.HasSupport; this._featureSupport[feature] = response.HasSupport;
} }

View file

@ -36,6 +36,7 @@ namespace Pulumi
Log.Debug(label); Log.Debug(label);
// Be resilient to misbehaving callers. // Be resilient to misbehaving callers.
// ReSharper disable once ConstantNullCoalescingCondition
args ??= InvokeArgs.Empty; args ??= InvokeArgs.Empty;
// Wait for all values to be available, and then perform the RPC. // Wait for all values to be available, and then perform the RPC.

View file

@ -1,6 +1,5 @@
// Copyright 2016-2021, Pulumi Corporation // Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
@ -40,7 +39,7 @@ namespace Pulumi
// Wait for the parent to complete. // Wait for the parent to complete.
// If no parent was provided, parent to the root resource. // If no parent was provided, parent to the root resource.
LogExcessive($"Getting parent urn: t={type}, name={name}, custom={custom}, remote={remote}"); LogExcessive($"Getting parent urn: t={type}, name={name}, custom={custom}, remote={remote}");
var parentURN = options.Parent != null var parentUrn = options.Parent != null
? await options.Parent.Urn.GetValueAsync().ConfigureAwait(false) ? await options.Parent.Urn.GetValueAsync().ConfigureAwait(false)
: await GetRootResourceAsync(type).ConfigureAwait(false); : await GetRootResourceAsync(type).ConfigureAwait(false);
LogExcessive($"Got parent urn: t={type}, name={name}, custom={custom}, remote={remote}"); LogExcessive($"Got parent urn: t={type}, name={name}, custom={custom}, remote={remote}");
@ -53,25 +52,21 @@ namespace Pulumi
} }
var providerRefs = new Dictionary<string, string>(); var providerRefs = new Dictionary<string, string>();
if (remote) if (remote && options is ComponentResourceOptions componentOpts)
{ {
var componentOpts = options as ComponentResourceOptions; // If only the Provider opt is set, move it to the Providers list for further processing.
if (componentOpts != null) if (componentOpts.Provider != null && componentOpts.Providers.Count == 0)
{ {
// If only the Provider opt is set, move it to the Providers list for further processing. componentOpts.Providers.Add(componentOpts.Provider);
if (componentOpts.Provider != null && componentOpts.Providers.Count == 0) componentOpts.Provider = null;
{ }
componentOpts.Providers.Add(componentOpts.Provider);
componentOpts.Provider = null;
}
foreach (var provider in componentOpts.Providers) foreach (var provider in componentOpts.Providers)
{
var pref = await ProviderResource.RegisterAsync(provider).ConfigureAwait(false);
if (pref != null)
{ {
var pref = await ProviderResource.RegisterAsync(provider).ConfigureAwait(false); providerRefs.Add(provider.Package, pref);
if (pref != null)
{
providerRefs.Add(provider.Package, pref);
}
} }
} }
} }
@ -82,16 +77,16 @@ namespace Pulumi
// The list of all dependencies (implicit or explicit). // The list of all dependencies (implicit or explicit).
var allDirectDependencies = new HashSet<Resource>(explicitDirectDependencies); var allDirectDependencies = new HashSet<Resource>(explicitDirectDependencies);
var allDirectDependencyURNs = await GetAllTransitivelyReferencedCustomResourceURNsAsync(explicitDirectDependencies).ConfigureAwait(false); var allDirectDependencyUrns = await GetAllTransitivelyReferencedCustomResourceUrnsAsync(explicitDirectDependencies).ConfigureAwait(false);
var propertyToDirectDependencyURNs = new Dictionary<string, HashSet<string>>(); var propertyToDirectDependencyUrns = new Dictionary<string, HashSet<string>>();
foreach (var (propertyName, directDependencies) in propertyToDirectDependencies) foreach (var (propertyName, directDependencies) in propertyToDirectDependencies)
{ {
allDirectDependencies.AddRange(directDependencies); allDirectDependencies.AddRange(directDependencies);
var urns = await GetAllTransitivelyReferencedCustomResourceURNsAsync(directDependencies).ConfigureAwait(false); var urns = await GetAllTransitivelyReferencedCustomResourceUrnsAsync(directDependencies).ConfigureAwait(false);
allDirectDependencyURNs.AddRange(urns); allDirectDependencyUrns.AddRange(urns);
propertyToDirectDependencyURNs[propertyName] = urns; propertyToDirectDependencyUrns[propertyName] = urns;
} }
// Wait for all aliases. Note that we use 'res._aliases' instead of 'options.aliases' as // Wait for all aliases. Note that we use 'res._aliases' instead of 'options.aliases' as
@ -111,11 +106,11 @@ namespace Pulumi
return new PrepareResult( return new PrepareResult(
serializedProps, serializedProps,
parentURN ?? "", parentUrn ?? "",
providerRef ?? "", providerRef ?? "",
providerRefs, providerRefs,
allDirectDependencyURNs, allDirectDependencyUrns,
propertyToDirectDependencyURNs, propertyToDirectDependencyUrns,
aliases); aliases);
void LogExcessive(string message) void LogExcessive(string message)
@ -128,7 +123,7 @@ namespace Pulumi
private static Task<ImmutableArray<Resource>> GatherExplicitDependenciesAsync(InputList<Resource> resources) private static Task<ImmutableArray<Resource>> GatherExplicitDependenciesAsync(InputList<Resource> resources)
=> resources.ToOutput().GetValueAsync(); => resources.ToOutput().GetValueAsync();
private static async Task<HashSet<string>> GetAllTransitivelyReferencedCustomResourceURNsAsync( private static async Task<HashSet<string>> GetAllTransitivelyReferencedCustomResourceUrnsAsync(
HashSet<Resource> resources) HashSet<Resource> resources)
{ {
// Go through 'resources', but transitively walk through **Component** resources, // Go through 'resources', but transitively walk through **Component** resources,
@ -190,24 +185,24 @@ namespace Pulumi
} }
} }
private struct PrepareResult private readonly struct PrepareResult
{ {
public readonly Struct SerializedProps; public readonly Struct SerializedProps;
public readonly string ParentUrn; public readonly string ParentUrn;
public readonly string ProviderRef; public readonly string ProviderRef;
public readonly Dictionary<string, string> ProviderRefs; public readonly Dictionary<string, string> ProviderRefs;
public readonly HashSet<string> AllDirectDependencyURNs; public readonly HashSet<string> AllDirectDependencyUrns;
public readonly Dictionary<string, HashSet<string>> PropertyToDirectDependencyURNs; public readonly Dictionary<string, HashSet<string>> PropertyToDirectDependencyUrns;
public readonly List<string> Aliases; public readonly List<string> Aliases;
public PrepareResult(Struct serializedProps, string parentUrn, string providerRef, Dictionary<string, string> providerRefs, HashSet<string> allDirectDependencyURNs, Dictionary<string, HashSet<string>> propertyToDirectDependencyURNs, List<string> aliases) public PrepareResult(Struct serializedProps, string parentUrn, string providerRef, Dictionary<string, string> providerRefs, HashSet<string> allDirectDependencyUrns, Dictionary<string, HashSet<string>> propertyToDirectDependencyUrns, List<string> aliases)
{ {
SerializedProps = serializedProps; SerializedProps = serializedProps;
ParentUrn = parentUrn; ParentUrn = parentUrn;
ProviderRef = providerRef; ProviderRef = providerRef;
ProviderRefs = providerRefs; ProviderRefs = providerRefs;
AllDirectDependencyURNs = allDirectDependencyURNs; AllDirectDependencyUrns = allDirectDependencyUrns;
PropertyToDirectDependencyURNs = propertyToDirectDependencyURNs; PropertyToDirectDependencyUrns = propertyToDirectDependencyUrns;
Aliases = aliases; Aliases = aliases;
} }
} }

View file

@ -90,8 +90,9 @@ namespace Pulumi
resource, remote, newDependency, args, options).ConfigureAwait(false); resource, remote, newDependency, args, options).ConfigureAwait(false);
completionSources[Constants.UrnPropertyName].SetStringValue(response.urn, isKnown: true); completionSources[Constants.UrnPropertyName].SetStringValue(response.urn, isKnown: true);
if (resource is CustomResource customResource) if (resource is CustomResource)
{ {
// ReSharper disable once ConstantNullCoalescingCondition
var id = response.id ?? ""; var id = response.id ?? "";
completionSources[Constants.IdPropertyName].SetStringValue(id, isKnown: id != ""); completionSources[Constants.IdPropertyName].SetStringValue(id, isKnown: id != "");
} }

View file

@ -1,10 +1,8 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes; using Google.Protobuf.WellKnownTypes;
using Pulumi.Serialization;
using Pulumirpc; using Pulumirpc;
namespace Pulumi namespace Pulumi
@ -22,7 +20,6 @@ namespace Pulumi
var prepareResult = await this.PrepareResourceAsync( var prepareResult = await this.PrepareResourceAsync(
label, resource, custom: true, remote: false, args, options).ConfigureAwait(false); label, resource, custom: true, remote: false, args, options).ConfigureAwait(false);
var serializer = new Serializer(_excessiveDebugOutput);
Log.Debug($"ReadResource RPC prepared: id={id}, t={type}, name={name}" + Log.Debug($"ReadResource RPC prepared: id={id}, t={type}, name={name}" +
(_excessiveDebugOutput ? $", obj={prepareResult.SerializedProps}" : "")); (_excessiveDebugOutput ? $", obj={prepareResult.SerializedProps}" : ""));
@ -35,12 +32,12 @@ namespace Pulumi
Parent = prepareResult.ParentUrn, Parent = prepareResult.ParentUrn,
Provider = prepareResult.ProviderRef, Provider = prepareResult.ProviderRef,
Properties = prepareResult.SerializedProps, Properties = prepareResult.SerializedProps,
Version = options?.Version ?? "", Version = options.Version ?? "",
AcceptSecrets = true, AcceptSecrets = true,
AcceptResources = !_disableResourceReferences, AcceptResources = !_disableResourceReferences,
}; };
request.Dependencies.AddRange(prepareResult.AllDirectDependencyURNs); request.Dependencies.AddRange(prepareResult.AllDirectDependencyUrns);
// Now run the operation, serializing the invocation if necessary. // Now run the operation, serializing the invocation if necessary.
var response = await this.Monitor.ReadResourceAsync(resource, request); var response = await this.Monitor.ReadResourceAsync(resource, request);

View file

@ -54,12 +54,12 @@ namespace Pulumi
request.Provider = prepareResult.ProviderRef; request.Provider = prepareResult.ProviderRef;
request.Providers.Add(prepareResult.ProviderRefs); request.Providers.Add(prepareResult.ProviderRefs);
request.Aliases.AddRange(prepareResult.Aliases); request.Aliases.AddRange(prepareResult.Aliases);
request.Dependencies.AddRange(prepareResult.AllDirectDependencyURNs); request.Dependencies.AddRange(prepareResult.AllDirectDependencyUrns);
foreach (var (key, resourceURNs) in prepareResult.PropertyToDirectDependencyURNs) foreach (var (key, resourceUrns) in prepareResult.PropertyToDirectDependencyUrns)
{ {
var deps = new RegisterResourceRequest.Types.PropertyDependencies(); var deps = new RegisterResourceRequest.Types.PropertyDependencies();
deps.Urns.AddRange(resourceURNs); deps.Urns.AddRange(resourceUrns);
request.PropertyDependencies.Add(key, deps); request.PropertyDependencies.Add(key, deps);
} }
} }
@ -70,7 +70,7 @@ namespace Pulumi
var customOpts = options as CustomResourceOptions; var customOpts = options as CustomResourceOptions;
var deleteBeforeReplace = customOpts?.DeleteBeforeReplace; var deleteBeforeReplace = customOpts?.DeleteBeforeReplace;
var request = new RegisterResourceRequest() var request = new RegisterResourceRequest
{ {
Type = type, Type = type,
Name = name, Name = name,
@ -113,7 +113,7 @@ namespace Pulumi
// Simply put, we simply convert our ticks to the integral number of nanoseconds // Simply put, we simply convert our ticks to the integral number of nanoseconds
// corresponding to it. Since each tick is 100ns, this can trivialy be done just by // corresponding to it. Since each tick is 100ns, this can trivialy be done just by
// appending "00" to it. // appending "00" to it.
return timeSpan.Value.Ticks.ToString() + "00ns"; return timeSpan.Value.Ticks + "00ns";
} }
} }
} }

View file

@ -1,7 +1,6 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Google.Protobuf; using Google.Protobuf;
using Pulumirpc; using Pulumirpc;
@ -22,7 +21,7 @@ namespace Pulumi
private async Task RegisterResourceOutputsAsync( private async Task RegisterResourceOutputsAsync(
Resource resource, Output<IDictionary<string, object?>> outputs) Resource resource, Output<IDictionary<string, object?>> outputs)
{ {
var opLabel = $"monitor.registerResourceOutputs(...)"; var opLabel = "monitor.registerResourceOutputs(...)";
// The registration could very well still be taking place, so we will need to wait for its URN. // The registration could very well still be taking place, so we will need to wait for its URN.
// Additionally, the output properties might have come from other resources, so we must await those too. // Additionally, the output properties might have come from other resources, so we must await those too.

View file

@ -9,7 +9,7 @@ namespace Pulumi
public partial class Deployment public partial class Deployment
{ {
private Task<string>? _rootResource; private Task<string>? _rootResource;
private object _rootResourceLock = new object(); private readonly object _rootResourceLock = new object();
/// <summary> /// <summary>
/// Returns a root resource URN that will automatically become the default parent of all /// Returns a root resource URN that will automatically become the default parent of all

View file

@ -12,7 +12,7 @@ namespace Pulumi
public partial class Deployment public partial class Deployment
{ {
/// <summary> /// <summary>
/// <see cref="RunAsync(Func{Task{IDictionary{string, object}}}, StackOptions)"/> for more details. /// <see cref="RunAsync(Func{Task{IDictionary{string,object}}}, StackOptions)"/> for more details.
/// </summary> /// </summary>
/// <param name="action">Callback that creates stack resources.</param> /// <param name="action">Callback that creates stack resources.</param>
public static Task<int> RunAsync(Action action) public static Task<int> RunAsync(Action action)
@ -174,7 +174,7 @@ namespace Pulumi
return engine.Errors.Count switch return engine.Errors.Count switch
{ {
1 => throw new RunException(engine.Errors.Single()), 1 => throw new RunException(engine.Errors.Single()),
int v when v > 1 => throw new AggregateException(engine.Errors.Select(e => new RunException(e))), var v when v > 1 => throw new AggregateException(engine.Errors.Select(e => new RunException(e))),
_ => monitor.Resources.ToImmutableArray() _ => monitor.Resources.ToImmutableArray()
}; };
} }

View file

@ -1,7 +1,6 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -65,7 +64,7 @@ namespace Pulumi
propertyToDependentResources.ToImmutable()); propertyToDependentResources.ToImmutable());
} }
private struct SerializationResult private readonly struct SerializationResult
{ {
public readonly Struct Serialized; public readonly Struct Serialized;
public readonly ImmutableDictionary<string, HashSet<Resource>> PropertyToDependentResources; public readonly ImmutableDictionary<string, HashSet<Resource>> PropertyToDependentResources;

View file

@ -1,7 +1,7 @@
// Copyright 2016-2020, Pulumi Corporation // Copyright 2016-2020, Pulumi Corporation
using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Grpc.Core; using Grpc.Core;
using Pulumirpc; using Pulumirpc;

View file

@ -1,7 +1,7 @@
// Copyright 2016-2020, Pulumi Corporation // Copyright 2016-2020, Pulumi Corporation
using System.Threading.Tasks;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks;
using Grpc.Core; using Grpc.Core;
using Pulumirpc; using Pulumirpc;

View file

@ -3,8 +3,7 @@
namespace Pulumi namespace Pulumi
{ {
/// <summary> /// <summary>
/// Options to help control the behavior of <see cref="IDeployment.InvokeAsync{T}(string, /// Options to help control the behavior of <see cref="IDeployment.InvokeAsync{T}(string, InvokeArgs, InvokeOptions)"/>.
/// InvokeArgs, InvokeOptions)"/>.
/// </summary> /// </summary>
public class InvokeOptions public class InvokeOptions
{ {

View file

@ -12,8 +12,8 @@ namespace Pulumi
/// </summary> /// </summary>
public class ResourceException : Exception public class ResourceException : Exception
{ {
internal readonly Resource? Resource; internal Resource? Resource { get; }
internal readonly bool HideStack; internal bool HideStack { get; }
public ResourceException(string message, Resource? resource, bool hideStack = false) : base(message) public ResourceException(string message, Resource? resource, bool hideStack = false) : base(message)
{ {

View file

@ -21,7 +21,7 @@ namespace Pulumi
return result; return result;
} }
public static void Deconstruct<K, V>(this KeyValuePair<K, V> pair, out K key, out V value) public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> pair, out TKey key, out TValue value)
{ {
key = pair.Key; key = pair.Key;
value = pair.Value; value = pair.Value;
@ -43,6 +43,7 @@ namespace Pulumi
{ {
_ = response.ContinueWith(t => _ = response.ContinueWith(t =>
{ {
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
switch (t.Status) switch (t.Status)
{ {
default: throw new InvalidOperationException("Task was not complete: " + t.Status); default: throw new InvalidOperationException("Task was not complete: " + t.Status);

View file

@ -1,6 +1,5 @@
// Copyright 2016-2019, Pulumi Corporation // Copyright 2016-2019, Pulumi Corporation
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace Pulumi namespace Pulumi
@ -20,7 +19,7 @@ namespace Pulumi
/// </summary> /// </summary>
public List<ProviderResource> Providers public List<ProviderResource> Providers
{ {
get => _providers ?? (_providers = new List<ProviderResource>()); get => _providers ??= new List<ProviderResource>();
set => _providers = value; set => _providers = value;
} }

View file

@ -26,7 +26,7 @@ namespace Pulumi
/// </summary> /// </summary>
public List<string> AdditionalSecretOutputs public List<string> AdditionalSecretOutputs
{ {
get => _additionalSecretOutputs ?? (_additionalSecretOutputs = new List<string>()); get => _additionalSecretOutputs ??= new List<string>();
set => _additionalSecretOutputs = value; set => _additionalSecretOutputs = value;
} }

View file

@ -16,13 +16,13 @@ namespace Pulumi
public DependencyProviderResource(string reference) public DependencyProviderResource(string reference)
: base(package: "", name: "", args: ResourceArgs.Empty, dependency: true) : base(package: "", name: "", args: ResourceArgs.Empty, dependency: true)
{ {
int lastSep = reference.LastIndexOf("::", StringComparison.Ordinal); var lastSep = reference.LastIndexOf("::", StringComparison.Ordinal);
if (lastSep == -1) if (lastSep == -1)
{ {
throw new ArgumentException($"Expected \"::\" in provider reference ${reference}."); throw new ArgumentException($"Expected \"::\" in provider reference ${reference}.");
} }
string urn = reference.Substring(0, lastSep); var urn = reference.Substring(0, lastSep);
string id = reference.Substring(lastSep + 2); var id = reference.Substring(lastSep + 2);
var resources = ImmutableHashSet.Create<Resource>(this); var resources = ImmutableHashSet.Create<Resource>(this);

View file

@ -35,14 +35,15 @@ namespace Pulumi
var all = fieldQuery.Concat(propQuery).ToList(); var all = fieldQuery.Concat(propQuery).ToList();
foreach (var (attr, memberName, memberType, getValue) in all) foreach (var (_, memberName, memberType, _) in all)
{ {
var fullName = $"[Input] {this.GetType().FullName}.{memberName}"; var fullName = $"[Input] {this.GetType().FullName}.{memberName}";
// ReSharper disable once VirtualMemberCallInConstructor
ValidateMember(memberType, fullName); ValidateMember(memberType, fullName);
} }
_inputInfos = all.Select(t => _inputInfos = all.Select(t =>
new InputInfo(t.attr, t.memberName, t.memberType, t.getValue)).ToImmutableArray(); new InputInfo(t.attr!, t.memberName, t.memberType, t.getValue)).ToImmutableArray();
} }
internal virtual async Task<ImmutableDictionary<string, object?>> ToDictionaryAsync() internal virtual async Task<ImmutableDictionary<string, object?>> ToDictionaryAsync()
@ -80,14 +81,15 @@ namespace Pulumi
return JsonFormatter.Default.Format(value); return JsonFormatter.Default.Format(value);
} }
private struct InputInfo private readonly struct InputInfo
{ {
public readonly InputAttribute Attribute; public readonly InputAttribute Attribute;
// ReSharper disable once NotAccessedField.Local
public readonly Type MemberType; public readonly Type MemberType;
public readonly string MemberName; public readonly string MemberName;
public Func<object, object?> GetValue; public readonly Func<object, object?> GetValue;
public InputInfo(InputAttribute attribute, string memberName, Type memberType, Func<object, object> getValue) : this() public InputInfo(InputAttribute attribute, string memberName, Type memberType, Func<object, object?> getValue) : this()
{ {
Attribute = attribute; Attribute = attribute;
MemberName = memberName; MemberName = memberName;

View file

@ -11,10 +11,6 @@ namespace Pulumi
{ {
public static readonly InvokeArgs Empty = new EmptyInvokeArgs(); public static readonly InvokeArgs Empty = new EmptyInvokeArgs();
protected InvokeArgs()
{
}
private protected override void ValidateMember(Type memberType, string fullName) private protected override void ValidateMember(Type memberType, string fullName)
{ {
if (typeof(IInput).IsAssignableFrom(memberType)) if (typeof(IInput).IsAssignableFrom(memberType))

View file

@ -12,7 +12,7 @@ namespace Pulumi
/// </summary> /// </summary>
public class ProviderResource : CustomResource public class ProviderResource : CustomResource
{ {
internal readonly string Package; internal string Package { get; }
private string? _registrationId; private string? _registrationId;
@ -53,14 +53,14 @@ namespace Pulumi
if (provider._registrationId == null) if (provider._registrationId == null)
{ {
var providerURN = await provider.Urn.GetValueAsync().ConfigureAwait(false); var providerUrn = await provider.Urn.GetValueAsync().ConfigureAwait(false);
var providerID = await provider.Id.GetValueAsync().ConfigureAwait(false); var providerId = await provider.Id.GetValueAsync().ConfigureAwait(false);
if (string.IsNullOrEmpty(providerID)) if (string.IsNullOrEmpty(providerId))
{ {
providerID = Constants.UnknownValue; providerId = Constants.UnknownValue;
} }
provider._registrationId = $"{providerURN}::{providerID}"; provider._registrationId = $"{providerUrn}::{providerId}";
} }
return provider._registrationId; return provider._registrationId;

View file

@ -16,11 +16,6 @@ namespace Pulumi
private readonly string _type; private readonly string _type;
private readonly string _name; private readonly string _name;
/// <summary>
/// The optional parent of this resource.
/// </summary>
private readonly Resource? _parentResource;
/// <summary> /// <summary>
/// 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
@ -56,7 +51,7 @@ namespace Pulumi
/// 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.
/// </summary> /// </summary>
internal readonly HashSet<Resource> ChildResources = new HashSet<Resource>(); internal HashSet<Resource> ChildResources { get; } = new HashSet<Resource>();
/// <summary> /// <summary>
/// Urn is the stable logical URN used to distinctly address a resource, both before and /// Urn is the stable logical URN used to distinctly address a resource, both before and
@ -187,14 +182,13 @@ namespace Pulumi
if (options.Parent != null) if (options.Parent != null)
{ {
this._parentResource = options.Parent; var parentResource = options.Parent;
lock (this._parentResource.ChildResources) lock (parentResource.ChildResources)
{ {
this._parentResource.ChildResources.Add(this); parentResource.ChildResources.Add(this);
} }
if (options.Protect == null) options.Protect ??= options.Parent._protect;
options.Protect = options.Parent._protect;
// Make a copy of the aliases array, and add to it any implicit aliases inherited from its parent // Make a copy of the aliases array, and add to it any implicit aliases inherited from its parent
options.Aliases = options.Aliases.ToList(); options.Aliases = options.Aliases.ToList();
@ -331,7 +325,6 @@ $"Only specify one of '{nameof(Alias.Parent)}', '{nameof(Alias.ParentUrn)}' or '
if (value != null) if (value != null)
{ {
ThrowAliasPropertyConflict(name); ThrowAliasPropertyConflict(name);
return;
} }
} }

View file

@ -11,10 +11,6 @@ namespace Pulumi
{ {
public static readonly ResourceArgs Empty = new EmptyResourceArgs(); public static readonly ResourceArgs Empty = new EmptyResourceArgs();
protected ResourceArgs()
{
}
private protected override void ValidateMember(Type memberType, string fullName) private protected override void ValidateMember(Type memberType, string fullName)
{ {
// No validation. A member may or may not be IInput. // No validation. A member may or may not be IInput.

View file

@ -26,7 +26,7 @@ namespace Pulumi
/// </summary> /// </summary>
public InputList<Resource> DependsOn public InputList<Resource> DependsOn
{ {
get => _dependsOn ?? (_dependsOn = new InputList<Resource>()); get => _dependsOn ??= new InputList<Resource>();
set => _dependsOn = value; set => _dependsOn = value;
} }
@ -42,7 +42,7 @@ namespace Pulumi
/// </summary> /// </summary>
public List<string> IgnoreChanges public List<string> IgnoreChanges
{ {
get => _ignoreChanges ?? (_ignoreChanges = new List<string>()); get => _ignoreChanges ??= new List<string>();
set => _ignoreChanges = value; set => _ignoreChanges = value;
} }
@ -78,7 +78,7 @@ namespace Pulumi
/// </summary> /// </summary>
public List<ResourceTransformation> ResourceTransformations public List<ResourceTransformation> ResourceTransformations
{ {
get => _resourceTransformations ?? (_resourceTransformations = new List<ResourceTransformation>()); get => _resourceTransformations ??= new List<ResourceTransformation>();
set => _resourceTransformations = value; set => _resourceTransformations = value;
} }

View file

@ -4,7 +4,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using Pulumi.Serialization;
namespace Pulumi namespace Pulumi
{ {
@ -152,6 +151,6 @@ namespace Pulumi
/// The name of the stack to reference. /// The name of the stack to reference.
/// </summary> /// </summary>
[Input("name", required: true)] [Input("name", required: true)]
public Input<string>? Name { get; set; } = null!; public Input<string>? Name { get; set; }
} }
} }

View file

@ -8,6 +8,10 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text.Json; using System.Text.Json;
using Google.Protobuf.WellKnownTypes; using Google.Protobuf.WellKnownTypes;
using Enum = System.Enum;
using Type = System.Type;
// ReSharper disable TailRecursiveCall
namespace Pulumi.Serialization namespace Pulumi.Serialization
{ {
@ -19,15 +23,15 @@ namespace Pulumi.Serialization
return new OutputData<T>(ImmutableHashSet<Resource>.Empty, (T)data!, isKnown, isSecret); return new OutputData<T>(ImmutableHashSet<Resource>.Empty, (T)data!, isKnown, isSecret);
} }
public static OutputData<object?> ConvertValue(string context, Value value, System.Type targetType) public static OutputData<object?> ConvertValue(string context, Value value, Type targetType)
{ {
return ConvertValue(context, value, targetType, ImmutableHashSet<Resource>.Empty); return ConvertValue(context, value, targetType, ImmutableHashSet<Resource>.Empty);
} }
public static OutputData<object?> ConvertValue( public static OutputData<object?> ConvertValue(
string context, Value value, System.Type targetType, ImmutableHashSet<Resource> resources) string context, Value value, Type targetType, ImmutableHashSet<Resource> resources)
{ {
CheckTargetType(context, targetType, new HashSet<System.Type>()); CheckTargetType(context, targetType, new HashSet<Type>());
var (deserialized, isKnown, isSecret) = Deserializer.Deserialize(value); var (deserialized, isKnown, isSecret) = Deserializer.Deserialize(value);
var converted = ConvertObject(context, deserialized, targetType); var converted = ConvertObject(context, deserialized, targetType);
@ -35,7 +39,7 @@ namespace Pulumi.Serialization
return new OutputData<object?>(resources, converted, isKnown, isSecret); return new OutputData<object?>(resources, converted, isKnown, isSecret);
} }
private static object? ConvertObject(string context, object? val, System.Type targetType) private static object? ConvertObject(string context, object? val, Type targetType)
{ {
var (result, exception) = TryConvertObject(context, val, targetType); var (result, exception) = TryConvertObject(context, val, targetType);
if (exception != null) if (exception != null)
@ -44,7 +48,7 @@ namespace Pulumi.Serialization
return result; return result;
} }
private static (object?, InvalidOperationException?) TryConvertObject(string context, object? val, System.Type targetType) private static (object?, InvalidOperationException?) TryConvertObject(string context, object? val, Type targetType)
{ {
var targetIsNullable = targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>); var targetIsNullable = targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>);
@ -116,7 +120,7 @@ namespace Pulumi.Serialization
if (exception != null || value is null) if (exception != null || value is null)
return (null, exception); return (null, exception);
return (System.Enum.ToObject(targetType, value), null); return (Enum.ToObject(targetType, value), null);
} }
if (targetType.IsValueType && targetType.GetCustomAttribute<EnumTypeAttribute>() != null) if (targetType.IsValueType && targetType.GetCustomAttribute<EnumTypeAttribute>() != null)
@ -136,7 +140,7 @@ namespace Pulumi.Serialization
return (null, new InvalidOperationException( return (null, new InvalidOperationException(
$"Expected target type {targetType.FullName} to have a constructor with a single {valType.FullName} parameter.")); $"Expected target type {targetType.FullName} to have a constructor with a single {valType.FullName} parameter."));
} }
return (enumTypeConstructor.Invoke(new object[] { val }), null); return (enumTypeConstructor.Invoke(new[] { val }), null);
} }
if (targetType.IsConstructedGenericType) if (targetType.IsConstructedGenericType)
@ -154,14 +158,14 @@ namespace Pulumi.Serialization
$"Unexpected generic target type {targetType.FullName} when deserializing {context}"); $"Unexpected generic target type {targetType.FullName} when deserializing {context}");
} }
if (targetType.GetCustomAttribute<Pulumi.OutputTypeAttribute>() == null) if (targetType.GetCustomAttribute<OutputTypeAttribute>() == null)
return (null, new InvalidOperationException( return (null, new InvalidOperationException(
$"Unexpected target type {targetType.FullName} when deserializing {context}")); $"Unexpected target type {targetType.FullName} when deserializing {context}"));
var constructor = GetPropertyConstructor(targetType); var constructor = GetPropertyConstructor(targetType);
if (constructor == null) if (constructor == null)
return (null, new InvalidOperationException( return (null, new InvalidOperationException(
$"Expected target type {targetType.FullName} to have [{nameof(Pulumi.OutputConstructorAttribute)}] constructor when deserializing {context}")); $"Expected target type {targetType.FullName} to have [{nameof(OutputConstructorAttribute)}] constructor when deserializing {context}"));
var (dictionary, tempException) = TryEnsureType<ImmutableDictionary<string, object>>(context, val); var (dictionary, tempException) = TryEnsureType<ImmutableDictionary<string, object>>(context, val);
if (tempException != null) if (tempException != null)
@ -191,20 +195,18 @@ namespace Pulumi.Serialization
private static (object?, InvalidOperationException?) TryConvertJsonElement( private static (object?, InvalidOperationException?) TryConvertJsonElement(
string context, object val) string context, object val)
{ {
using (var stream = new MemoryStream()) using var stream = new MemoryStream();
using (var writer = new Utf8JsonWriter(stream))
{ {
using (var writer = new Utf8JsonWriter(stream)) var exception = TryWriteJson(context, writer, val);
{ if (exception != null)
var exception = TryWriteJson(context, writer, val); return (null, exception);
if (exception != null)
return (null, exception);
}
stream.Position = 0;
var document = JsonDocument.Parse(stream);
var element = document.RootElement;
return (element, null);
} }
stream.Position = 0;
var document = JsonDocument.Parse(stream);
var element = document.RootElement;
return (element, null);
} }
private static InvalidOperationException? TryWriteJson(string context, Utf8JsonWriter writer, object? val) private static InvalidOperationException? TryWriteJson(string context, Utf8JsonWriter writer, object? val)
@ -252,7 +254,7 @@ namespace Pulumi.Serialization
private static (T, InvalidOperationException?) TryEnsureType<T>(string context, object val) private static (T, InvalidOperationException?) TryEnsureType<T>(string context, object val)
=> val is T t ? (t, null) : (default(T)!, new InvalidOperationException($"Expected {typeof(T).FullName} but got {val.GetType().FullName} deserializing {context}")); => val is T t ? (t, null) : (default(T)!, new InvalidOperationException($"Expected {typeof(T).FullName} but got {val.GetType().FullName} deserializing {context}"));
private static (object?, InvalidOperationException?) TryConvertOneOf(string context, object val, System.Type oneOfType) private static (object?, InvalidOperationException?) TryConvertOneOf(string context, object val, Type oneOfType)
{ {
var firstType = oneOfType.GenericTypeArguments[0]; var firstType = oneOfType.GenericTypeArguments[0];
var secondType = oneOfType.GenericTypeArguments[1]; var secondType = oneOfType.GenericTypeArguments[1];
@ -275,14 +277,14 @@ namespace Pulumi.Serialization
} }
private static (object?, InvalidOperationException?) TryConvertArray( private static (object?, InvalidOperationException?) TryConvertArray(
string fieldName, object val, System.Type targetType) string fieldName, object val, Type targetType)
{ {
if (!(val is ImmutableArray<object> array)) if (!(val is ImmutableArray<object> array))
return (null, new InvalidOperationException( return (null, new InvalidOperationException(
$"Expected {typeof(ImmutableArray<object>).FullName} but got {val.GetType().FullName} deserializing {fieldName}")); $"Expected {typeof(ImmutableArray<object>).FullName} but got {val.GetType().FullName} deserializing {fieldName}"));
var builder = var builder =
typeof(ImmutableArray).GetMethod(nameof(ImmutableArray.CreateBuilder), Array.Empty<System.Type>())! typeof(ImmutableArray).GetMethod(nameof(ImmutableArray.CreateBuilder), Array.Empty<Type>())!
.MakeGenericMethod(targetType.GenericTypeArguments) .MakeGenericMethod(targetType.GenericTypeArguments)
.Invoke(obj: null, parameters: null)!; .Invoke(obj: null, parameters: null)!;
@ -303,7 +305,7 @@ namespace Pulumi.Serialization
} }
private static (object?, InvalidOperationException?) TryConvertDictionary( private static (object?, InvalidOperationException?) TryConvertDictionary(
string fieldName, object val, System.Type targetType) string fieldName, object val, Type targetType)
{ {
if (!(val is ImmutableDictionary<string, object> dictionary)) if (!(val is ImmutableDictionary<string, object> dictionary))
return (null, new InvalidOperationException( return (null, new InvalidOperationException(
@ -319,7 +321,7 @@ namespace Pulumi.Serialization
$"Unexpected type {targetType.FullName} when deserializing {fieldName}. ImmutableDictionary's TKey type was not {typeof(string).FullName}")); $"Unexpected type {targetType.FullName} when deserializing {fieldName}. ImmutableDictionary's TKey type was not {typeof(string).FullName}"));
var builder = var builder =
typeof(ImmutableDictionary).GetMethod(nameof(ImmutableDictionary.CreateBuilder), Array.Empty<System.Type>())! typeof(ImmutableDictionary).GetMethod(nameof(ImmutableDictionary.CreateBuilder), Array.Empty<Type>())!
.MakeGenericMethod(targetType.GenericTypeArguments) .MakeGenericMethod(targetType.GenericTypeArguments)
.Invoke(obj: null, parameters: null)!; .Invoke(obj: null, parameters: null)!;
@ -341,7 +343,7 @@ namespace Pulumi.Serialization
return (builderToImmutable.Invoke(builder, null), null); return (builderToImmutable.Invoke(builder, null), null);
} }
public static void CheckTargetType(string context, System.Type targetType, HashSet<System.Type> seenTypes) public static void CheckTargetType(string context, Type targetType, HashSet<Type> seenTypes)
{ {
// types can be recursive. So only dive into a type if it's the first time we're seeing it. // types can be recursive. So only dive into a type if it's the first time we're seeing it.
if (!seenTypes.Add(targetType)) if (!seenTypes.Add(targetType))
@ -398,7 +400,7 @@ namespace Pulumi.Serialization
throw new InvalidOperationException( throw new InvalidOperationException(
$"{targetType.FullName} had [{nameof(EnumTypeAttribute)}], but did not contain constructor with a single String or Double parameter."); $"{targetType.FullName} had [{nameof(EnumTypeAttribute)}], but did not contain constructor with a single String or Double parameter.");
static bool CheckEnumType(System.Type targetType, System.Type underlyingType) static bool CheckEnumType(Type targetType, Type underlyingType)
{ {
var constructor = targetType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { underlyingType }, null); var constructor = targetType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { underlyingType }, null);
if (constructor == null) if (constructor == null)
@ -424,39 +426,34 @@ namespace Pulumi.Serialization
CheckTargetType(context, targetType.GenericTypeArguments.Single(), seenTypes); CheckTargetType(context, targetType.GenericTypeArguments.Single(), seenTypes);
return; return;
} }
else if (targetType.GetGenericTypeDefinition() == typeof(Union<,>)) if (targetType.GetGenericTypeDefinition() == typeof(Union<,>))
{ {
CheckTargetType(context, targetType.GenericTypeArguments[0], seenTypes); CheckTargetType(context, targetType.GenericTypeArguments[0], seenTypes);
CheckTargetType(context, targetType.GenericTypeArguments[1], seenTypes); CheckTargetType(context, targetType.GenericTypeArguments[1], seenTypes);
return; return;
} }
else if (targetType.GetGenericTypeDefinition() == typeof(ImmutableArray<>)) if (targetType.GetGenericTypeDefinition() == typeof(ImmutableArray<>))
{ {
CheckTargetType(context, targetType.GenericTypeArguments.Single(), seenTypes); CheckTargetType(context, targetType.GenericTypeArguments.Single(), seenTypes);
return; return;
} }
else if (targetType.GetGenericTypeDefinition() == typeof(ImmutableDictionary<,>)) if (targetType.GetGenericTypeDefinition() == typeof(ImmutableDictionary<,>))
{ {
var dictTypeArgs = targetType.GenericTypeArguments; var dictTypeArgs = targetType.GenericTypeArguments;
if (dictTypeArgs[0] != typeof(string)) if (dictTypeArgs[0] != typeof(string))
{ {
throw new InvalidOperationException( throw new InvalidOperationException($@"{context} contains invalid type {targetType.FullName}:
$@"{context} contains invalid type {targetType.FullName}:
The only allowed ImmutableDictionary 'TKey' type is 'String'."); The only allowed ImmutableDictionary 'TKey' type is 'String'.");
} }
CheckTargetType(context, dictTypeArgs[1], seenTypes); CheckTargetType(context, dictTypeArgs[1], seenTypes);
return; return;
} }
else throw new InvalidOperationException($@"{context} contains invalid type {targetType.FullName}:
{
throw new InvalidOperationException(
$@"{context} contains invalid type {targetType.FullName}:
The only generic types allowed are ImmutableArray<...> and ImmutableDictionary<string, ...>"); The only generic types allowed are ImmutableArray<...> and ImmutableDictionary<string, ...>");
}
} }
var propertyTypeAttribute = (Attribute?)targetType.GetCustomAttribute<OutputTypeAttribute>(); var propertyTypeAttribute = targetType.GetCustomAttribute<OutputTypeAttribute>();
if (propertyTypeAttribute == null) if (propertyTypeAttribute == null)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
@ -479,8 +476,8 @@ $@"{targetType.FullName} had [{nameof(OutputTypeAttribute)}], but did not contai
} }
} }
private static ConstructorInfo GetPropertyConstructor(System.Type outputTypeArg) private static ConstructorInfo? GetPropertyConstructor(Type outputTypeArg)
=> outputTypeArg.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault( => outputTypeArg.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(
c => c.GetCustomAttributes<OutputConstructorAttribute>() != null); c => c.GetCustomAttribute<OutputConstructorAttribute>() != null);
} }
} }

Some files were not shown because too many files have changed in this diff Show more