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");