From 9cdcd77015417eaaa2d2b531508546a16e2b4731 Mon Sep 17 00:00:00 2001 From: Matt Ellis Date: Wed, 20 Jun 2018 14:16:02 -0700 Subject: [PATCH] Remove Pulumi.Host and CSI, in favor of `dotnet run` This feels more natrual, even if the code you end up writing to describe your application has a little more ceremony. We also don't have to worry about all the crazy things we likely would have had to worry about if we continued down the CSI path, or some path where we had a middle stage that reflection loaded an actual binary and invoked into it (I had a fear in the back of my mind that at some point we'd actually have to start using AssemblyLoadContext). This model is pretty easy to internalize, as well. The major change from the language plugin point of view is that instead of passing command line arguments to the executor, we just set a bunch of `PULUMI_XXX` env-vars, which parts of our system know how to use. Next up, Output tracking. --- sdk/dotnet/Pulumi.Host/.vscode/launch.json | 28 -------- sdk/dotnet/Pulumi.Host/.vscode/tasks.json | 27 ------- sdk/dotnet/Pulumi.Host/Program.cs | 63 ---------------- .../Pulumi.Host/pulumi-language-dotnet-exec | 3 - .../pulumi-language-dotnet-exec.csproj | 25 ------- sdk/dotnet/Pulumi/Deployment.cs | 40 +++++++++++ sdk/dotnet/Pulumi/Runtime.cs | 3 +- sdk/dotnet/build-and-run.sh | 5 +- sdk/dotnet/cmd/pulumi-language-dotnet/main.go | 71 +++++++------------ sdk/dotnet/dotnet.sln | 14 ---- sdk/dotnet/examples/bucket/Program.cs | 40 +++++++++++ .../{ => bucket}/Pulumi.hello-dotnet.yaml | 0 sdk/dotnet/examples/{ => bucket}/Pulumi.yaml | 0 sdk/dotnet/examples/bucket/bucket.csproj | 12 ++++ sdk/dotnet/examples/main.csx | 35 --------- 15 files changed, 120 insertions(+), 246 deletions(-) delete mode 100644 sdk/dotnet/Pulumi.Host/.vscode/launch.json delete mode 100644 sdk/dotnet/Pulumi.Host/.vscode/tasks.json delete mode 100644 sdk/dotnet/Pulumi.Host/Program.cs delete mode 100755 sdk/dotnet/Pulumi.Host/pulumi-language-dotnet-exec delete mode 100644 sdk/dotnet/Pulumi.Host/pulumi-language-dotnet-exec.csproj create mode 100644 sdk/dotnet/Pulumi/Deployment.cs create mode 100644 sdk/dotnet/examples/bucket/Program.cs rename sdk/dotnet/examples/{ => bucket}/Pulumi.hello-dotnet.yaml (100%) rename sdk/dotnet/examples/{ => bucket}/Pulumi.yaml (100%) create mode 100644 sdk/dotnet/examples/bucket/bucket.csproj delete mode 100644 sdk/dotnet/examples/main.csx diff --git a/sdk/dotnet/Pulumi.Host/.vscode/launch.json b/sdk/dotnet/Pulumi.Host/.vscode/launch.json deleted file mode 100644 index ae2693219..000000000 --- a/sdk/dotnet/Pulumi.Host/.vscode/launch.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - // Use IntelliSense to find out which attributes exist for C# debugging - // Use hover for the description of the existing attributes - // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md - "version": "0.2.0", - "configurations": [ - { - "name": ".NET Core Launch (console)", - "type": "coreclr", - "request": "launch", - "preLaunchTask": "build", - // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/bin/Debug/netcoreapp2.0/DummyHost.dll", - "args": [], - "cwd": "${workspaceFolder}", - // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window - "console": "internalConsole", - "stopAtEntry": false, - "internalConsoleOptions": "openOnSessionStart" - }, - { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach", - "processId": "${command:pickProcess}" - } - ] -} \ No newline at end of file diff --git a/sdk/dotnet/Pulumi.Host/.vscode/tasks.json b/sdk/dotnet/Pulumi.Host/.vscode/tasks.json deleted file mode 100644 index cae878570..000000000 --- a/sdk/dotnet/Pulumi.Host/.vscode/tasks.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "${workspaceFolder}/pulumi-language-dotnet-exec.csproj" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "publish", - "command": "dotnet", - "type": "process", - "args": [ - "publish", - "-r", - "linux-x64", - "${workspaceFolder}/pulumi-language-dotnet-exec.csproj" - ], - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file diff --git a/sdk/dotnet/Pulumi.Host/Program.cs b/sdk/dotnet/Pulumi.Host/Program.cs deleted file mode 100644 index c24175cc3..000000000 --- a/sdk/dotnet/Pulumi.Host/Program.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Grpc.Core; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Scripting; -using Microsoft.CodeAnalysis.Scripting; -using Microsoft.CodeAnalysis.Scripting.Hosting; -using Mono.Options; -using Pulumi; -using Pulumirpc; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; -using System.Runtime.InteropServices; - -namespace Pulumi.Host -{ - class Program - { - static void Main(string[] args) - { - string monitor = ""; - string engine = ""; - string project = ""; - string stack = ""; - string pwd = ""; - string dryRun = ""; - int parallel = 1; - string tracing = ""; - - OptionSet o = new OptionSet { - {"monitor=", "", m => monitor = m }, - {"engine=", "", e => engine = e}, - {"project=", "", p => project = p}, - {"stack=", "", s => stack = s }, - {"pwd=", "", wd => pwd = wd}, - {"dry_run=", dry => dryRun = dry}, - {"parallel=", (int n) => parallel = n}, - {"tracing=", t => tracing = t}, - }; - - List extra = o.Parse(args); - - Channel engineChannel = new Channel(engine, ChannelCredentials.Insecure); - Channel monitorChannel = new Channel(monitor, ChannelCredentials.Insecure); - - Runtime.Initialize(new Runtime.Settings(new Engine.EngineClient(engineChannel), - new ResourceMonitor.ResourceMonitorClient(monitorChannel), - stack, project, parallel, true)); - - Console.WriteLine($"Running with \U0001F379 on {RuntimeInformation.FrameworkDescription} on {RuntimeInformation.OSDescription}"); - - Script script = CSharpScript.Create(File.OpenRead("main.csx")); - script.Compile(); - Runtime.RunInStack(() => { - script.RunAsync().Wait(); - }); - - engineChannel.ShutdownAsync().Wait(); - monitorChannel.ShutdownAsync().Wait(); - } - } -} - diff --git a/sdk/dotnet/Pulumi.Host/pulumi-language-dotnet-exec b/sdk/dotnet/Pulumi.Host/pulumi-language-dotnet-exec deleted file mode 100755 index d0fae5900..000000000 --- a/sdk/dotnet/Pulumi.Host/pulumi-language-dotnet-exec +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -dotnet "${DIR}/pulumi-language-dotnet-exec.dll" "$@" diff --git a/sdk/dotnet/Pulumi.Host/pulumi-language-dotnet-exec.csproj b/sdk/dotnet/Pulumi.Host/pulumi-language-dotnet-exec.csproj deleted file mode 100644 index 2bd436997..000000000 --- a/sdk/dotnet/Pulumi.Host/pulumi-language-dotnet-exec.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - Exe - netcoreapp2.0 - - - diff --git a/sdk/dotnet/Pulumi/Deployment.cs b/sdk/dotnet/Pulumi/Deployment.cs new file mode 100644 index 000000000..cdb9e565c --- /dev/null +++ b/sdk/dotnet/Pulumi/Deployment.cs @@ -0,0 +1,40 @@ +using Grpc.Core; +using Pulumi; +using Pulumirpc; +using System; +using System.IO; +using System.Threading.Tasks; +using System.Runtime.InteropServices; + +namespace Pulumi { + public static class Deployment { + + // TODO(ellismg): Perhaps we should have another overload Run(Func f) and we use reflection over the T + // to get all public fields and properties of type Input and set them as outputs? + public static void Run(Action a) { + + // TODO(ellismg): any one of these could be null, and we need to guard against that for ones that must + // be set (I don't know the set off the top of my head. I think that everything except tracing is + // required. Also, they could be bad values (e.g. parallel may not be something that can be `bool.Parsed` + // and we'd like to fail in a nicer manner. + string monitor = Environment.GetEnvironmentVariable("PULUMI_MONITOR"); + string engine = Environment.GetEnvironmentVariable("PULUMI_ENGINE"); + string project = Environment.GetEnvironmentVariable("PULUMI_PROJECT"); + string stack = Environment.GetEnvironmentVariable("PULUMI_STACK"); + string pwd = Environment.GetEnvironmentVariable("PULUMI_PWD"); + string dryRun = Environment.GetEnvironmentVariable("PULUMI_DRY_RUN"); + string parallel = Environment.GetEnvironmentVariable("PULUMI_PARALLEL"); + string tracing = Environment.GetEnvironmentVariable("PULUMI_TRACING"); + + Channel engineChannel = new Channel(engine, ChannelCredentials.Insecure); + Channel monitorChannel = new Channel(monitor, ChannelCredentials.Insecure); + + Runtime.Initialize(new Runtime.Settings(new Engine.EngineClient(engineChannel), + new ResourceMonitor.ResourceMonitorClient(monitorChannel), + stack, project, int.Parse(parallel), bool.Parse(dryRun))); + + Console.WriteLine($"Running with \U0001F379 on {RuntimeInformation.FrameworkDescription} on {RuntimeInformation.OSDescription}"); + Runtime.RunInStack(a); + } + } +} \ No newline at end of file diff --git a/sdk/dotnet/Pulumi/Runtime.cs b/sdk/dotnet/Pulumi/Runtime.cs index a04816d93..1ac96b46b 100644 --- a/sdk/dotnet/Pulumi/Runtime.cs +++ b/sdk/dotnet/Pulumi/Runtime.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Pulumirpc; namespace Pulumi @@ -29,7 +30,7 @@ namespace Pulumi public static void RunInStack(Action run) { Root = new ComponentResource("pulumi:pulumi:Stack", $"{Runtime.Project}-{Runtime.Stack}", null, ResourceOptions.None); - run(); + Task.Run(run).Wait(); } public class Settings diff --git a/sdk/dotnet/build-and-run.sh b/sdk/dotnet/build-and-run.sh index d065c29ee..16eac4005 100755 --- a/sdk/dotnet/build-and-run.sh +++ b/sdk/dotnet/build-and-run.sh @@ -3,7 +3,6 @@ set -eou pipefail IFS="\n\t" GOBIN=/opt/pulumi/bin go install ./cmd/pulumi-language-dotnet -dotnet publish Pulumi.Host/pulumi-language-dotnet-exec.csproj -export PATH=/opt/pulumi/bin:$(go env GOPATH)/src/github.com/pulumi/pulumi/sdk/dotnet/Pulumi.Host/bin/Debug/netcoreapp2.0/publish:$PATH -cd examples +export PATH=/opt/pulumi/bin:$PATH +cd examples/bucket pulumi update --diff --yes diff --git a/sdk/dotnet/cmd/pulumi-language-dotnet/main.go b/sdk/dotnet/cmd/pulumi-language-dotnet/main.go index 5a5ccdf31..34aa31e49 100644 --- a/sdk/dotnet/cmd/pulumi-language-dotnet/main.go +++ b/sdk/dotnet/cmd/pulumi-language-dotnet/main.go @@ -18,7 +18,6 @@ import ( "fmt" "os" "os/exec" - "path/filepath" "strings" "syscall" @@ -33,14 +32,6 @@ import ( "google.golang.org/grpc" ) -const ( - // By convention, the executor is the name of the current program (pulumi-language-dotnet) plus this suffix. - dotnetExecSuffix = "-exec" // the exec shim for Pulumi to run Python programs. - - // The runtime expects the config object to be saved to this environment variable. - pulumiConfigVar = "PULUMI_CONFIG" -) - // Launches the language host RPC endpoint, which in turn fires up an RPC server implementing the // LanguageRuntimeServer RPC endpoint. func main() { @@ -59,16 +50,9 @@ func main() { cmdutil.InitTracing("pulumi-language-dotnet", "pulumi-language-dotnet", tracing) var dotnetExec string if givenExecutor == "" { - // The -exec binary is the same name as the current language host, except that we must trim off - // the file extension (if any) and then append -exec to it. - bin := filepath.Base(os.Args[0]) - if ext := filepath.Ext(bin); ext != "" { - bin = bin[:len(bin)-len(ext)] - } - bin += dotnetExecSuffix - pathExec, err := exec.LookPath(bin) + pathExec, err := exec.LookPath("dotnet") if err != nil { - err = errors.Wrapf(err, "could not find `%s` on the $PATH", bin) + err = errors.Wrap(err, "could not find `dotnet` on the $PATH") cmdutil.Exit(err) } @@ -131,13 +115,18 @@ func (host *dotnetLanguageHost) GetRequiredPlugins(ctx context.Context, // RPC endpoint for LanguageRuntimeServer::Run func (host *dotnetLanguageHost) Run(ctx context.Context, req *pulumirpc.RunRequest) (*pulumirpc.RunResponse, error) { - args := host.constructArguments(req) config, err := host.constructConfig(req) if err != nil { err = errors.Wrap(err, "failed to serialize configuration") return nil, err } + args := []string{"run"} + + if req.GetProgram() != "" { + args = append(args, req.GetProgram()) + } + if glog.V(5) { commandStr := strings.Join(args, " ") glog.V(5).Infoln("Language host launching process: ", host.exec, commandStr) @@ -148,9 +137,7 @@ func (host *dotnetLanguageHost) Run(ctx context.Context, req *pulumirpc.RunReque cmd := exec.Command(host.exec, args...) // nolint: gas, intentionally running dynamic program name. cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - if config != "" { - cmd.Env = append(os.Environ(), pulumiConfigVar+"="+config) - } + cmd.Env = host.constructEnv(req, config) if err := cmd.Run(); err != nil { if exiterr, ok := err.(*exec.ExitError); ok { // If the program ran, but exited with a non-zero error code. This will happen often, since user @@ -172,36 +159,26 @@ func (host *dotnetLanguageHost) Run(ctx context.Context, req *pulumirpc.RunReque return &pulumirpc.RunResponse{Error: errResult}, nil } -// constructArguments constructs a command-line for `pulumi-language-dotnet` -// by enumerating all of the optional and non-optional arguments present -// in a RunRequest. -func (host *dotnetLanguageHost) constructArguments(req *pulumirpc.RunRequest) []string { - var args []string - maybeAppendArg := func(k, v string) { +func (host *dotnetLanguageHost) constructEnv(req *pulumirpc.RunRequest, config string) []string { + env := os.Environ() + + maybeAppendEnv := func(k, v string) { if v != "" { - args = append(args, "--"+k, v) + env = append(env, strings.ToUpper("PULUMI_"+k)+"="+v) } } - maybeAppendArg("monitor", req.GetMonitorAddress()) - maybeAppendArg("engine", host.engineAddress) - maybeAppendArg("project", req.GetProject()) - maybeAppendArg("stack", req.GetStack()) - maybeAppendArg("pwd", req.GetPwd()) - maybeAppendArg("dry_run", fmt.Sprintf("%v", req.GetDryRun())) - maybeAppendArg("parallel", fmt.Sprint(req.GetParallel())) - maybeAppendArg("tracing", host.tracing) - - // If no program is specified, just default to the current directory (which will invoke "__main__.py"). - if req.GetProgram() == "" { - args = append(args, ".") - } else { - args = append(args, req.GetProgram()) - } - - args = append(args, req.GetArgs()...) - return args + maybeAppendEnv("monitor", req.GetMonitorAddress()) + maybeAppendEnv("engine", host.engineAddress) + maybeAppendEnv("project", req.GetProject()) + maybeAppendEnv("stack", req.GetStack()) + maybeAppendEnv("pwd", req.GetPwd()) + maybeAppendEnv("dry_run", fmt.Sprintf("%v", req.GetDryRun())) + maybeAppendEnv("parallel", fmt.Sprint(req.GetParallel())) + maybeAppendEnv("tracing", host.tracing) + maybeAppendEnv("config", config) + return env } // constructConfig json-serializes the configuration data given as part of a RunRequest. diff --git a/sdk/dotnet/dotnet.sln b/sdk/dotnet/dotnet.sln index 1a067dca1..526d6a170 100644 --- a/sdk/dotnet/dotnet.sln +++ b/sdk/dotnet/dotnet.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pulumi", "Pulumi\Pulumi.csproj", "{0A2BFED8-13F3-43A4-A38B-B5D1651203EB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pulumi-language-dotnet-exec", "Pulumi.Host\pulumi-language-dotnet-exec.csproj", "{966A7C80-0122-423E-9A73-7AD4D1CDCFBE}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pulumirpc", "Pulumirpc\Pulumirpc.csproj", "{2B15CCF1-2D54-4E35-B42D-8747193FC70F}" EndProject Global @@ -34,18 +32,6 @@ Global {0A2BFED8-13F3-43A4-A38B-B5D1651203EB}.Release|x64.Build.0 = Release|Any CPU {0A2BFED8-13F3-43A4-A38B-B5D1651203EB}.Release|x86.ActiveCfg = Release|Any CPU {0A2BFED8-13F3-43A4-A38B-B5D1651203EB}.Release|x86.Build.0 = Release|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Debug|x64.ActiveCfg = Debug|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Debug|x64.Build.0 = Debug|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Debug|x86.ActiveCfg = Debug|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Debug|x86.Build.0 = Debug|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Release|Any CPU.Build.0 = Release|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Release|x64.ActiveCfg = Release|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Release|x64.Build.0 = Release|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Release|x86.ActiveCfg = Release|Any CPU - {966A7C80-0122-423E-9A73-7AD4D1CDCFBE}.Release|x86.Build.0 = Release|Any CPU {2B15CCF1-2D54-4E35-B42D-8747193FC70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2B15CCF1-2D54-4E35-B42D-8747193FC70F}.Debug|Any CPU.Build.0 = Debug|Any CPU {2B15CCF1-2D54-4E35-B42D-8747193FC70F}.Debug|x64.ActiveCfg = Debug|Any CPU diff --git a/sdk/dotnet/examples/bucket/Program.cs b/sdk/dotnet/examples/bucket/Program.cs new file mode 100644 index 000000000..cece3b14e --- /dev/null +++ b/sdk/dotnet/examples/bucket/Program.cs @@ -0,0 +1,40 @@ +using AWS.S3; +using Pulumi; +using System; +using System.Text; + +class Program +{ + static void Main(string[] args) + { + Deployment.Run(async () => { + Config config = new Config("hello-dotnet"); + + // Create the bucket, and make it readable. + var bucket = new Bucket(config["name"], new BucketArgs { + Acl = "public-read" + } + ); + + // Add some content. We can use contentBase64 for now, but next we'll want to build out the Assets pipeline so we + // can do a natural thing. + var content = new BucketObject($"{config["name"]}-content", new BucketObjectArgs { + Acl = "public-read", + Bucket = bucket, + ContentBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes("Made with \u2764, Pulumi, and .NET")), + ContentType = "text/plain; charset=utf8", + Key = "hello.txt" + } + ); + + // In addition to the logging here being nice, it actually forces us to block until the Tasks that represent the RPC + // calls to create the resources complete. + // + // TODO(ellismg): We need to come up with a solution here. We probably want to track all the pending tasks generated + // by Pulumi during execution and await them to complete in the host itself... + Console.WriteLine($"Bucket ID id : {await bucket.Id}"); + Console.WriteLine($"Content ID id : {await content.Id}"); + Console.WriteLine($"https://{await bucket.BucketDomainName}/hello.txt"); + }); + } +} diff --git a/sdk/dotnet/examples/Pulumi.hello-dotnet.yaml b/sdk/dotnet/examples/bucket/Pulumi.hello-dotnet.yaml similarity index 100% rename from sdk/dotnet/examples/Pulumi.hello-dotnet.yaml rename to sdk/dotnet/examples/bucket/Pulumi.hello-dotnet.yaml diff --git a/sdk/dotnet/examples/Pulumi.yaml b/sdk/dotnet/examples/bucket/Pulumi.yaml similarity index 100% rename from sdk/dotnet/examples/Pulumi.yaml rename to sdk/dotnet/examples/bucket/Pulumi.yaml diff --git a/sdk/dotnet/examples/bucket/bucket.csproj b/sdk/dotnet/examples/bucket/bucket.csproj new file mode 100644 index 000000000..e34987ea4 --- /dev/null +++ b/sdk/dotnet/examples/bucket/bucket.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp2.0 + + + + + + + diff --git a/sdk/dotnet/examples/main.csx b/sdk/dotnet/examples/main.csx deleted file mode 100644 index 77674e571..000000000 --- a/sdk/dotnet/examples/main.csx +++ /dev/null @@ -1,35 +0,0 @@ -#r "/home/matell/go/src/github.com/pulumi/pulumi/sdk/dotnet/Pulumi/bin/Debug/netstandard2.0/Pulumi.dll" -using AWS.S3; -using Pulumi; -using System; -using System.Text; -using System.Collections.Generic; - -Config config = new Config("hello-dotnet"); - -// Create the bucket, and make it readable. -var bucket = new Bucket(config["name"], new BucketArgs { - Acl = "public-read" - } -); - -// Add some content. We can use contentBase64 for now, but next we'll want to build out the Assets pipeline so we -// can do a natural thing. -var content = new BucketObject($"{config["name"]}-content", new BucketObjectArgs { - Acl = "public-read", - Bucket = bucket, - ContentBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes("Made with \u2764, Pulumi, and .NET")), - ContentType = "text/plain; charset=utf8", - Key = "hello.txt" - } -); - - -// In addition to the logging here being nice, it actually forces us to block until the Tasks that represent the RPC -// calls to create the resources complete. -// -// TODO(ellismg): We need to come up with a solution here. We probably want to track all the pending tasks generated -// by Pulumi during execution and await them to complete in the host itself... -Console.WriteLine($"Bucket ID id : {bucket.Id.Result}"); -Console.WriteLine($"Content ID id : {content.Id.Result}"); -Console.WriteLine($"https://{bucket.BucketDomainName.Result}/hello.txt");