[Automation API] - C# Implementation (#5761)

* Init Workspace interface for C# Automation API

* fleshing out workspace interface and beginning of local workspace implementation

* initial run pulumi cmd implementation

* resolve issue with pulumi cmd cleanup wrapper task after testing

* flesh out local workspace implementation, flesh out stack implementation, cleanup run pulumi cmd implementation and make it an instance so it is mockable/testable, separate serialization in prep for custom converters

* project settings json serialization implemented

* Initial commit of language server

* Add deployment from language server

* Cleanup

* finish json serialization

* project runtime yaml serialization completed. just need stack config value yaml serialization

* Remove typed argument

* Limit concurrency

* Rename file for consistency

* final commit of a semi-working project settings & stack settings serialization so that it is in the commit history

* modify workspace API so that settings accessors aren't fully exposed since we are defering a complete serialization implementation until a later date

* yaml converters wrap any outgoing exceptions so resolve that

* getting the beginning of inline program GRPC communication set up

* stack lifecycle operations implemented, and switched to newtonsoft for JSON serialization

* change back to system.text.json with a custom object converter

* local workspace tests written, working on getting them passing

* fix the encoding on the GO files used for testing

* all tests passing except inline program, pulumi engine not available with inline

* inline program engine is now running as expecting, but inline program is not recognizing local stack config

* All tests passing, but no concurrency capability because of the singleton DeploymentInstance.

* cleanup unnecessary usings

* minor cleanup / changes after a quick review. Make sure ConfigureAwait is used where needed. Remove newtonsoft dependency from testing. Update workspace API to use existing PluginKind enum. Modify LanguageRuntimeService so that its semaphore operates process-wide.

* support for parallel execution of inline program, test included

* Update LocalWorkspaceTests.cs

remove some redundancy from the inline program parallel execution text

* flesh out some comments and make asynclocal instance readonly

* Strip out instance locking since it is no longer necessary with AsyncLocal wrapping the Deployment.Instance. Modify CreateRunner method such that we are ensuring there isn't a chance of delayed synchronous execution polluting the value of Deployment.Instance across calls to Deployment.RunAsync

* resolve conflicts with changes made to Deployment.TestAsync entrypoints

* update changelog

* write a test that fails if the CreateRunnerAndRunAsync method on Deployment is not marked async and fix test project data file ref

* make resource package state share the lifetime of the deployment so that their isn't cross deployment issues with resource packages, add support and tests for external resource packages (resource packages that aren't referenced by the executing assembly)

* enable parallel test collection execution in test suite, add some additional tests for deployment instance protection and ensuring that our first class stack exceptions are thrown when expected

* minor inline project name arg change, and re-add xunit json to build output (whoops)

* strip out concurrency changes since they are now in #6139, split automation into separate assembly, split automation tests into separate assembly

* add copyright to the top of each new file

* resolve some PR remarks

* inline program exception is now properly propagated to the caller on UpAsync and PreviewAsync

* modify PulumiFn to allow TStack and other delegate overloads without needing multiple first class delegates.

* whoops missing a copyright

* resolve getting TStack into IRunner so that outputs are registered correctly and so that there isn't 2 instances of Pulumi.Stack instantiated.

* resolve issue with propagation of TStack exceptions and add a test

* add support for a TStack PulumiFn resolved via IServiceProvider

* update automation API description

* fix comment and remove unnecessary TODOs

* disable packaging of automation api assembly

* re-name automation api documentation file appropriately

* add --limit support to dotnet automation api for stack history per #6257

* re-name XStack as WorkspaceStack

* replace --limit usage with --page-size and --page in dotnet automation api per #6292

Co-authored-by: evanboyle <evan@pulumi.com>
Co-authored-by: Josh Studt <josh.studt@figmarketing.com>
Co-authored-by: Dan Friedman <dan@thefriedmans.org>
Co-authored-by: David Ferretti <David.Ferretti@figmarketing.com>
Co-authored-by: Mikhail Shilkov <github@mikhail.io>
This commit is contained in:
Josh Studt 2021-02-18 05:36:21 -05:00 committed by GitHub
parent 8d5e9bcd6e
commit 963b5ab710
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
100 changed files with 6838 additions and 12 deletions

View file

@ -2,7 +2,9 @@ CHANGELOG
=========
## HEAD (Unreleased)
_(none)_
[sdk/dotnet] C# Automation API.
[#5761](https://github.com/pulumi/pulumi/pull/5761)
## 2.21.0 (2021-02-17)

View file

@ -1,4 +1,5 @@
[Bb]in/
[Oo]bj/
.leu
Pulumi/Pulumi.xml
Pulumi/Pulumi.xml
*.user

View file

@ -0,0 +1,6 @@
// Copyright 2016-2021, Pulumi Corporation
using Xunit;
// Unfortunately, we depend on static state. So for now disable parallelization.
[assembly: CollectionBehavior(DisableTestParallelization = true)]

View file

@ -0,0 +1,124 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Pulumi.Automation.Commands.Exceptions;
using Xunit;
namespace Pulumi.Automation.Tests
{
public class CommandExceptionTests
{
private static string GetTestSuffix()
{
var random = new Random();
var result = 100000 + random.Next(0, 900000);
return result.ToString();
}
[Fact]
public async Task StackNotFoundExceptionIsThrown()
{
var projectSettings = new ProjectSettings("command_exception_test", ProjectRuntimeName.NodeJS);
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{
ProjectSettings = projectSettings,
});
var stackName = $"non_existent_stack{GetTestSuffix()}";
var selectTask = workspace.SelectStackAsync(stackName);
await Assert.ThrowsAsync<StackNotFoundException>(
() => selectTask);
}
[Fact]
public async Task StackAlreadyExistsExceptionIsThrown()
{
var projectSettings = new ProjectSettings("command_exception_test", ProjectRuntimeName.NodeJS);
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{
ProjectSettings = projectSettings,
});
var stackName = $"already_existing_stack{GetTestSuffix()}";
await workspace.CreateStackAsync(stackName);
try
{
var createTask = workspace.CreateStackAsync(stackName);
await Assert.ThrowsAsync<StackAlreadyExistsException>(
() => createTask);
}
finally
{
await workspace.RemoveStackAsync(stackName);
}
}
[Fact]
public async Task ConcurrentUpdateExceptionIsThrown()
{
var projectSettings = new ProjectSettings("command_exception_test", ProjectRuntimeName.NodeJS);
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{
ProjectSettings = projectSettings,
});
var stackName = $"concurrent_update_stack{GetTestSuffix()}";
await workspace.CreateStackAsync(stackName);
try
{
var stack = await WorkspaceStack.SelectAsync(stackName, workspace);
var hitSemaphore = false;
using var semaphore = new SemaphoreSlim(0, 1);
var program = PulumiFn.Create(() =>
{
hitSemaphore = true;
semaphore.Wait();
return new Dictionary<string, object?>()
{
["test"] = "doesnt matter",
};
});
var upTask = stack.UpAsync(new UpOptions
{
Program = program,
});
// wait until we hit semaphore
while (!hitSemaphore)
{
await Task.Delay(TimeSpan.FromSeconds(2));
if (upTask.IsFaulted)
throw upTask.Exception!;
else if (upTask.IsCompleted)
throw new Exception("never hit semaphore in first UP task");
}
// before releasing the semaphore, ensure another up throws
var concurrentTask = stack.UpAsync(new UpOptions
{
Program = program, // should never make it into this
});
await Assert.ThrowsAsync<ConcurrentUpdateException>(
() => concurrentTask);
// finish first up call
semaphore.Release();
await upTask;
}
finally
{
await workspace.RemoveStackAsync(stackName);
}
}
}
}

View file

@ -0,0 +1,9 @@
{
"secretsProvider": "abc",
"config": {
"plain": "plain",
"secure": {
"secure": "secret"
}
}
}

View file

@ -0,0 +1,5 @@
{
"name": "testproj",
"runtime": "go",
"description": "A minimal Go Pulumi program"
}

View file

@ -0,0 +1,3 @@
name: testproj
runtime: go
description: A minimal Go Pulumi program

View file

@ -0,0 +1,5 @@
module testproj
go 1.14
require github.com/pulumi/pulumi/sdk/v2 v2.0.0

View file

@ -0,0 +1,261 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cheggaaa/pb v1.0.18 h1:G/DgkKaBP0V5lnBg/vx61nVxxAU+VqU5yMzSc0f2PPE=
github.com/cheggaaa/pb v1.0.18/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/djherbis/times v1.2.0 h1:xANXjsC/iBqbO00vkWlYwPWgBgEVU6m6AFYg0Pic+Mc=
github.com/djherbis/times v1.2.0/go.mod h1:CGMZlo255K5r4Yw0b9RRfFQpM2y7uOmxg4jm9HsaVf8=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/pulumi/pulumi/sdk/v2 v2.0.0/go.mod h1:W7k1UDYerc5o97mHnlHHp5iQZKEby+oQrQefWt+2RF4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/texttheater/golang-levenshtein v0.0.0-20191208221605-eb6844b05fc6 h1:9VTskZOIRf2vKF3UL8TuWElry5pgUpV1tFSe/e/0m/E=
github.com/texttheater/golang-levenshtein v0.0.0-20191208221605-eb6844b05fc6/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/uber/jaeger-client-go v2.22.1+incompatible h1:NHcubEkVbahf9t3p75TOCR83gdUHXjRJvjoBh1yACsM=
github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/GfSYVCjK7dyaw=
github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6 h1:TjszyFsQsyZNHwdVdZ5m7bjmreu0znc2kRYsEml9/Ww=
golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d h1:62ap6LNOjDU6uGmKXHJbSfciMoV+FeI1sRXx/pLDL44=
golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0 h1:ucqkfpjg9WzSUubAO62csmucvxl4/JeW3F4I4909XkM=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

View file

@ -0,0 +1,16 @@
package main
import (
"github.com/pulumi/pulumi/sdk/v2/go/pulumi"
"github.com/pulumi/pulumi/sdk/v2/go/pulumi/config"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
c := config.New(ctx, "")
ctx.Export("exp_static", pulumi.String("foo"))
ctx.Export("exp_cfg", pulumi.String(c.Get("bar")))
ctx.Export("exp_secret", c.GetSecret("buzz"))
return nil
})
}

View file

@ -0,0 +1,5 @@
secretsProvider: abc
config:
plain: plain
secure:
secure: secret

View file

@ -0,0 +1,3 @@
name: testproj
runtime: go
description: A minimal Go Pulumi program

View file

@ -0,0 +1,5 @@
secretsProvider: abc
config:
plain: plain
secure:
secure: secret

View file

@ -0,0 +1,3 @@
name: testproj
runtime: go
description: A minimal Go Pulumi program

View file

@ -0,0 +1,646 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Pulumi.Automation.Commands.Exceptions;
using Xunit;
namespace Pulumi.Automation.Tests
{
public class LocalWorkspaceTests
{
private static readonly string _dataDirectory =
Path.Combine(new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName, "Data");
private static string GetTestSuffix()
{
var random = new Random();
var result = 100000 + random.Next(0, 900000);
return result.ToString();
}
private static string NormalizeConfigKey(string key, string projectName)
{
var parts = key.Split(":");
if (parts.Length < 2)
return $"{projectName}:{key}";
return string.Empty;
}
[Theory]
[InlineData("yaml")]
[InlineData("yml")]
[InlineData("json")]
public async Task GetProjectSettings(string extension)
{
var workingDir = Path.Combine(_dataDirectory, extension);
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{
WorkDir = workingDir,
});
var settings = await workspace.GetProjectSettingsAsync();
Assert.NotNull(settings);
Assert.Equal("testproj", settings!.Name);
Assert.Equal(ProjectRuntimeName.Go, settings.Runtime.Name);
Assert.Equal("A minimal Go Pulumi program", settings.Description);
}
[Theory]
[InlineData("yaml")]
[InlineData("yml")]
[InlineData("json")]
public async Task GetStackSettings(string extension)
{
var workingDir = Path.Combine(_dataDirectory, extension);
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{
WorkDir = workingDir,
});
var settings = await workspace.GetStackSettingsAsync("dev");
Assert.NotNull(settings);
Assert.Equal("abc", settings!.SecretsProvider);
Assert.NotNull(settings.Config);
Assert.True(settings.Config!.TryGetValue("plain", out var plainValue));
Assert.Equal("plain", plainValue!.Value);
Assert.False(plainValue.IsSecure);
Assert.True(settings.Config.TryGetValue("secure", out var secureValue));
Assert.Equal("secret", secureValue!.Value);
Assert.True(secureValue.IsSecure);
}
[Fact]
public async Task AddRemoveListPlugins()
{
using var workspace = await LocalWorkspace.CreateAsync();
var plugins = await workspace.ListPluginsAsync();
if (plugins.Any(p => p.Name == "aws" && p.Version == "3.0.0"))
{
await workspace.RemovePluginAsync("aws", "3.0.0");
plugins = await workspace.ListPluginsAsync();
Assert.DoesNotContain(plugins, p => p.Name == "aws" && p.Version == "3.0.0");
}
await workspace.InstallPluginAsync("aws", "v3.0.0");
plugins = await workspace.ListPluginsAsync();
var aws = plugins.FirstOrDefault(p => p.Name == "aws" && p.Version == "3.0.0");
Assert.NotNull(aws);
await workspace.RemovePluginAsync("aws", "3.0.0");
plugins = await workspace.ListPluginsAsync();
Assert.DoesNotContain(plugins, p => p.Name == "aws" && p.Version == "3.0.0");
}
[Fact]
public async Task CreateSelectRemoveStack()
{
var projectSettings = new ProjectSettings("node_test", ProjectRuntimeName.NodeJS);
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{
ProjectSettings = projectSettings,
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
var stackName = $"int_test{GetTestSuffix()}";
var stacks = await workspace.ListStacksAsync();
Assert.Empty(stacks);
await workspace.CreateStackAsync(stackName);
stacks = await workspace.ListStacksAsync();
var newStack = stacks.FirstOrDefault(s => s.Name == stackName);
Assert.NotNull(newStack);
Assert.True(newStack.IsCurrent);
await workspace.SelectStackAsync(stackName);
await workspace.RemoveStackAsync(stackName);
stacks = await workspace.ListStacksAsync();
Assert.Empty(stacks);
}
[Fact]
public async Task ManipulateConfig()
{
var projectName = "node_test";
var projectSettings = new ProjectSettings(projectName, ProjectRuntimeName.NodeJS);
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{
ProjectSettings = projectSettings,
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
var stackName = $"int_test{GetTestSuffix()}";
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 secretKey = NormalizeConfigKey("secret", projectName);
await Assert.ThrowsAsync<CommandException>(
() => stack.GetConfigValueAsync(plainKey));
var values = await stack.GetConfigAsync();
Assert.Empty(values);
await stack.SetConfigAsync(config);
values = await stack.GetConfigAsync();
Assert.True(values.TryGetValue(plainKey, out var plainValue));
Assert.Equal("abc", plainValue!.Value);
Assert.False(plainValue.IsSecret);
Assert.True(values.TryGetValue(secretKey, out var secretValue));
Assert.Equal("def", secretValue!.Value);
Assert.True(secretValue.IsSecret);
await stack.RemoveConfigValueAsync("plain");
values = await stack.GetConfigAsync();
Assert.Single(values);
await stack.SetConfigValueAsync("foo", new ConfigValue("bar"));
values = await stack.GetConfigAsync();
Assert.Equal(2, values.Count);
await workspace.RemoveStackAsync(stackName);
}
[Fact]
public async Task ListStackAndCurrentlySelected()
{
var projectSettings = new ProjectSettings(
$"node_list_test{GetTestSuffix()}",
ProjectRuntimeName.NodeJS);
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{
ProjectSettings = projectSettings,
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
var stackNames = new List<string>();
try
{
for (var i = 0; i < 2; i++)
{
var stackName = GetStackName();
await WorkspaceStack.CreateAsync(stackName, workspace);
stackNames.Add(stackName);
var summary = await workspace.GetStackAsync();
Assert.NotNull(summary);
Assert.True(summary!.IsCurrent);
var stacks = await workspace.ListStacksAsync();
Assert.Equal(i + 1, stacks.Count);
}
}
finally
{
foreach (var name in stackNames)
await workspace.RemoveStackAsync(name);
}
static string GetStackName()
=> $"int_test{GetTestSuffix()}";
}
[Fact]
public async Task CheckStackStatus()
{
var projectSettings = new ProjectSettings("node_test", ProjectRuntimeName.NodeJS);
using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions
{
ProjectSettings = projectSettings,
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
var stackName = $"int_test{GetTestSuffix()}";
var stack = await WorkspaceStack.CreateAsync(stackName, workspace);
try
{
var history = await stack.GetHistoryAsync();
Assert.Empty(history);
var info = await stack.GetInfoAsync();
Assert.Null(info);
}
finally
{
await workspace.RemoveStackAsync(stackName);
}
}
[Fact]
public async Task StackLifecycleLocalProgram()
{
var stackName = $"int_test{GetTestSuffix()}";
var workingDir = Path.Combine(_dataDirectory, "testproj");
using var stack = await LocalWorkspace.CreateStackAsync(new LocalProgramArgs(stackName, workingDir)
{
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
var config = new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
await stack.SetConfigAsync(config);
// pulumi up
var upResult = await stack.UpAsync();
Assert.Equal(UpdateKind.Update, upResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, upResult.Summary.Result);
Assert.Equal(3, upResult.Outputs.Count);
// exp_static
Assert.True(upResult.Outputs.TryGetValue("exp_static", out var expStaticValue));
Assert.Equal("foo", expStaticValue!.Value);
Assert.False(expStaticValue.IsSecret);
// exp_cfg
Assert.True(upResult.Outputs.TryGetValue("exp_cfg", out var expConfigValue));
Assert.Equal("abc", expConfigValue!.Value);
Assert.False(expConfigValue.IsSecret);
// exp_secret
Assert.True(upResult.Outputs.TryGetValue("exp_secret", out var expSecretValue));
Assert.Equal("secret", expSecretValue!.Value);
Assert.True(expSecretValue.IsSecret);
// pulumi preview
await stack.PreviewAsync();
// TODO: update assertions when we have structured output
// pulumi refresh
var refreshResult = await stack.RefreshAsync();
Assert.Equal(UpdateKind.Refresh, refreshResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, refreshResult.Summary.Result);
// pulumi destroy
var destroyResult = await stack.DestroyAsync();
Assert.Equal(UpdateKind.Destroy, destroyResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, destroyResult.Summary.Result);
await stack.Workspace.RemoveStackAsync(stackName);
}
[Fact]
public async Task StackLifecycleInlineProgram()
{
var program = PulumiFn.Create(() =>
{
var config = new Pulumi.Config();
return new Dictionary<string, object?>
{
["exp_static"] = "foo",
["exp_cfg"] = config.Get("bar"),
["exp_secret"] = config.GetSecret("buzz"),
};
});
var stackName = $"int_test{GetTestSuffix()}";
var projectName = "inline_node";
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
var config = new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
await stack.SetConfigAsync(config);
// pulumi up
var upResult = await stack.UpAsync();
Assert.Equal(UpdateKind.Update, upResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, upResult.Summary.Result);
Assert.Equal(3, upResult.Outputs.Count);
// exp_static
Assert.True(upResult.Outputs.TryGetValue("exp_static", out var expStaticValue));
Assert.Equal("foo", expStaticValue!.Value);
Assert.False(expStaticValue.IsSecret);
// exp_cfg
Assert.True(upResult.Outputs.TryGetValue("exp_cfg", out var expConfigValue));
Assert.Equal("abc", expConfigValue!.Value);
Assert.False(expConfigValue.IsSecret);
// exp_secret
Assert.True(upResult.Outputs.TryGetValue("exp_secret", out var expSecretValue));
Assert.Equal("secret", expSecretValue!.Value);
Assert.True(expSecretValue.IsSecret);
// pulumi preview
await stack.PreviewAsync();
// TODO: update assertions when we have structured output
// pulumi refresh
var refreshResult = await stack.RefreshAsync();
Assert.Equal(UpdateKind.Refresh, refreshResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, refreshResult.Summary.Result);
// pulumi destroy
var destroyResult = await stack.DestroyAsync();
Assert.Equal(UpdateKind.Destroy, destroyResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, destroyResult.Summary.Result);
await stack.Workspace.RemoveStackAsync(stackName);
}
private class ValidStack : Stack
{
[Output("exp_static")]
public Output<string> ExpStatic { get; set; }
[Output("exp_cfg")]
public Output<string> ExpConfig { get; set; }
[Output("exp_secret")]
public Output<string> ExpSecret { get; set; }
public ValidStack()
{
var config = new Pulumi.Config();
this.ExpStatic = Output.Create("foo");
this.ExpConfig = Output.Create(config.Get("bar")!);
this.ExpSecret = config.GetSecret("buzz")!;
}
}
[Fact]
public async Task StackLifecycleInlineProgramWithTStack()
{
var program = PulumiFn.Create<ValidStack>();
var stackName = $"int_test{GetTestSuffix()}";
var projectName = "inline_tstack_node";
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
var config = new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("abc"),
["buzz"] = new ConfigValue("secret", isSecret: true),
};
await stack.SetConfigAsync(config);
// pulumi up
var upResult = await stack.UpAsync();
Assert.Equal(UpdateKind.Update, upResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, upResult.Summary.Result);
Assert.Equal(3, upResult.Outputs.Count);
// exp_static
Assert.True(upResult.Outputs.TryGetValue("exp_static", out var expStaticValue));
Assert.Equal("foo", expStaticValue!.Value);
Assert.False(expStaticValue.IsSecret);
// exp_cfg
Assert.True(upResult.Outputs.TryGetValue("exp_cfg", out var expConfigValue));
Assert.Equal("abc", expConfigValue!.Value);
Assert.False(expConfigValue.IsSecret);
// exp_secret
Assert.True(upResult.Outputs.TryGetValue("exp_secret", out var expSecretValue));
Assert.Equal("secret", expSecretValue!.Value);
Assert.True(expSecretValue.IsSecret);
// pulumi preview
await stack.PreviewAsync();
// TODO: update assertions when we have structured output
// pulumi refresh
var refreshResult = await stack.RefreshAsync();
Assert.Equal(UpdateKind.Refresh, refreshResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, refreshResult.Summary.Result);
// pulumi destroy
var destroyResult = await stack.DestroyAsync();
Assert.Equal(UpdateKind.Destroy, destroyResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, destroyResult.Summary.Result);
await stack.Workspace.RemoveStackAsync(stackName);
}
[Fact]
public async Task InlineProgramExceptionPropagatesToCaller()
{
const string projectName = "exception_inline_node";
var stackName = $"int_test_{GetTestSuffix()}";
var program = PulumiFn.Create((Action)(() => throw new FileNotFoundException()));
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
var upTask = stack.UpAsync();
await Assert.ThrowsAsync<FileNotFoundException>(
() => upTask);
}
private class FileNotFoundStack : Pulumi.Stack
{
public FileNotFoundStack()
{
throw new FileNotFoundException();
}
}
[Fact]
public async Task InlineProgramExceptionPropagatesToCallerWithTStack()
{
const string projectName = "exception_inline_tstack_node";
var stackName = $"int_test_{GetTestSuffix()}";
var program = PulumiFn.Create<FileNotFoundStack>();
using var stack = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectName, stackName, program)
{
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
var upTask = stack.UpAsync();
await Assert.ThrowsAsync<FileNotFoundException>(
() => upTask);
}
[Fact(Skip = "Parallel execution is not supported in this first version.")]
public async Task InlineProgramAllowsParallelExecution()
{
const string projectNameOne = "parallel_inline_node1";
const string projectNameTwo = "parallel_inline_node2";
var stackNameOne = $"int_test1_{GetTestSuffix()}";
var stackNameTwo = $"int_test2_{GetTestSuffix()}";
var hasReachedSemaphoreOne = false;
using var semaphoreOne = new SemaphoreSlim(0, 1);
var programOne = PulumiFn.Create(() =>
{
// we want to assert before and after each interaction with
// the semaphore because we want to alternately stutter
// programOne and programTwo so we can assert they aren't
// touching eachothers instances
var config = new Pulumi.Config();
Assert.Equal(projectNameOne, Deployment.Instance.ProjectName);
Assert.Equal(stackNameOne, Deployment.Instance.StackName);
hasReachedSemaphoreOne = true;
semaphoreOne.Wait();
Assert.Equal(projectNameOne, Deployment.Instance.ProjectName);
Assert.Equal(stackNameOne, Deployment.Instance.StackName);
return new Dictionary<string, object?>
{
["exp_static"] = "1",
["exp_cfg"] = config.Get("bar"),
["exp_secret"] = config.GetSecret("buzz"),
};
});
var hasReachedSemaphoreTwo = false;
using var semaphoreTwo = new SemaphoreSlim(0, 1);
var programTwo = PulumiFn.Create(() =>
{
var config = new Pulumi.Config();
Assert.Equal(projectNameTwo, Deployment.Instance.ProjectName);
Assert.Equal(stackNameTwo, Deployment.Instance.StackName);
hasReachedSemaphoreTwo = true;
semaphoreTwo.Wait();
Assert.Equal(projectNameTwo, Deployment.Instance.ProjectName);
Assert.Equal(stackNameTwo, Deployment.Instance.StackName);
return new Dictionary<string, object?>
{
["exp_static"] = "2",
["exp_cfg"] = config.Get("bar"),
["exp_secret"] = config.GetSecret("buzz"),
};
});
using var stackOne = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectNameOne, stackNameOne, programOne)
{
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
using var stackTwo = await LocalWorkspace.CreateStackAsync(new InlineProgramArgs(projectNameTwo, stackNameTwo, programTwo)
{
EnvironmentVariables = new Dictionary<string, string>()
{
["PULUMI_CONFIG_PASSPHRASE"] = "test",
}
});
await stackOne.SetConfigAsync(new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("1"),
["buzz"] = new ConfigValue("1", isSecret: true),
});
await stackTwo.SetConfigAsync(new Dictionary<string, ConfigValue>()
{
["bar"] = new ConfigValue("2"),
["buzz"] = new ConfigValue("2", isSecret: true),
});
var upTaskOne = stackOne.UpAsync();
// wait until we hit semaphore one
while (!hasReachedSemaphoreOne)
{
await Task.Delay(TimeSpan.FromSeconds(2));
if (upTaskOne.IsFaulted)
throw upTaskOne.Exception!;
else if (upTaskOne.IsCompleted)
throw new Exception("Never hit semaphore in first UP task.");
}
var upTaskTwo = stackTwo.UpAsync();
// wait until we hit semaphore two
while (!hasReachedSemaphoreTwo)
{
await Task.Delay(TimeSpan.FromSeconds(2));
if (upTaskTwo.IsFaulted)
throw upTaskTwo.Exception!;
else if (upTaskTwo.IsCompleted)
throw new Exception("Never hit semaphore in second UP task.");
}
// alternately allow them to progress
semaphoreOne.Release();
var upResultOne = await upTaskOne;
semaphoreTwo.Release();
var upResultTwo = await upTaskTwo;
AssertUpResult(upResultOne, "1");
AssertUpResult(upResultTwo, "2");
static void AssertUpResult(UpResult upResult, string value)
{
Assert.Equal(UpdateKind.Update, upResult.Summary.Kind);
Assert.Equal(UpdateState.Succeeded, upResult.Summary.Result);
Assert.Equal(3, upResult.Outputs.Count);
// exp_static
Assert.True(upResult.Outputs.TryGetValue("exp_static", out var expStaticValue));
Assert.Equal(value, expStaticValue!.Value);
Assert.False(expStaticValue.IsSecret);
// exp_cfg
Assert.True(upResult.Outputs.TryGetValue("exp_cfg", out var expConfigValue));
Assert.Equal(value, expConfigValue!.Value);
Assert.False(expConfigValue.IsSecret);
// exp_secret
Assert.True(upResult.Outputs.TryGetValue("exp_secret", out var expSecretValue));
Assert.Equal(value, expSecretValue!.Value);
Assert.True(expSecretValue.IsSecret);
}
}
}
}

View file

@ -0,0 +1,60 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Moq" Version="4.13.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Pulumi.Automation\Pulumi.Automation.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\json\Pulumi.dev.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\json\Pulumi.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\testproj\go.mod">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\testproj\go.sum">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\testproj\main.go">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\testproj\Pulumi.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\yaml\Pulumi.dev.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\yaml\Pulumi.yaml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\yml\Pulumi.dev.yml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Data\yml\Pulumi.yml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Data\" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,52 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic;
using Pulumi.Automation.Serialization;
using Xunit;
namespace Pulumi.Automation.Tests.Serialization
{
public class DynamicObjectTests
{
private static LocalSerializer _serializer = new LocalSerializer();
[Fact]
public void Dynamic_With_YamlDotNet()
{
const string yaml = @"
one: 123
two: two
three: true
nested:
test: test
testtwo: 123
";
var dict = _serializer.DeserializeYaml<Dictionary<string, object>>(yaml);
Assert.NotNull(dict);
Assert.NotEmpty(dict);
Assert.Equal(4, dict.Count);
}
[Fact]
public void Dynamic_With_SystemTextJson()
{
const string json = @"
{
""one"": 123,
""two"": ""two"",
""three"": true,
""nested"": {
""test"": ""test"",
""testtwo"": 123,
}
}
";
var dict = _serializer.DeserializeJson<Dictionary<string, object>>(json);
Assert.NotNull(dict);
Assert.NotEmpty(dict);
Assert.Equal(4, dict.Count);
}
}
}

View file

@ -0,0 +1,140 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using Pulumi.Automation.Serialization;
using Xunit;
namespace Pulumi.Automation.Tests.Serialization
{
public class GeneralJsonConverterTests
{
private static LocalSerializer _serializer = new LocalSerializer();
[Fact]
public void CanDeserializeConfigValue()
{
var json = @"
{
""aws:region"": {
""value"": ""us-east-1"",
""secret"": false,
},
""project:name"": {
""value"": ""test"",
""secret"": true,
}
}
";
var config = _serializer.DeserializeJson<Dictionary<string, ConfigValue>>(json);
Assert.NotNull(config);
Assert.True(config.TryGetValue("aws:region", out var regionValue));
Assert.Equal("us-east-1", regionValue!.Value);
Assert.False(regionValue.IsSecret);
Assert.True(config.TryGetValue("project:name", out var secretValue));
Assert.Equal("test", secretValue!.Value);
Assert.True(secretValue.IsSecret);
}
[Fact]
public void CanDeserializePluginInfo()
{
var json = @"
{
""name"": ""aws"",
""kind"": ""resource"",
""version"": ""3.19.2"",
""size"": 258460028,
""installTime"": ""2020-12-09T19:24:23.214Z"",
""lastUsedTime"": ""2020-12-09T19:24:26.059Z""
}
";
var installTime = new DateTime(2020, 12, 9, 19, 24, 23, 214);
var lastUsedTime = new DateTime(2020, 12, 9, 19, 24, 26, 059);
var info = _serializer.DeserializeJson<PluginInfo>(json);
Assert.NotNull(info);
Assert.Equal("aws", info.Name);
Assert.Equal(PluginKind.Resource, info.Kind);
Assert.Equal(258460028, info.Size);
Assert.Equal(new DateTimeOffset(installTime, TimeSpan.Zero), info.InstallTime);
Assert.Equal(new DateTimeOffset(lastUsedTime, TimeSpan.Zero), info.LastUsedTime);
}
[Fact]
public void CanDeserializeUpdateSummary()
{
var json = @"
[
{
""kind"": ""destroy"",
""startTime"": ""2021-01-07T17:08:49.000Z"",
""message"": """",
""environment"": {
""exec.kind"": ""cli""
},
""config"": {
""aws:region"": {
""value"": ""us-east-1"",
""secret"": false
},
""quickstart:test"": {
""value"": ""okok"",
""secret"": true
}
},
""result"": ""in-progress"",
""endTime"": ""2021-01-07T17:09:14.000Z"",
""resourceChanges"": {
""delete"": 3
}
},
{
""kind"": ""update"",
""startTime"": ""2021-01-07T17:02:10.000Z"",
""message"": """",
""environment"": {
""exec.kind"": ""cli""
},
""config"": {
""aws:region"": {
""value"": ""us-east-1"",
""secret"": false
},
""quickstart:test"": {
""value"": ""okok"",
""secret"": true
}
},
""result"": ""succeeded"",
""endTime"": ""2021-01-07T17:02:24.000Z"",
""resourceChanges"": {
""create"": 3
}
}
]
";
var history = _serializer.DeserializeJson<List<UpdateSummary>>(json);
Assert.NotNull(history);
Assert.Equal(2, history.Count);
var destroy = history[0];
Assert.Equal(UpdateKind.Destroy, destroy.Kind);
Assert.Equal(UpdateState.InProgress, destroy.Result);
Assert.NotNull(destroy.ResourceChanges);
Assert.Equal(1, destroy.ResourceChanges!.Count);
Assert.True(destroy.ResourceChanges.TryGetValue(OperationType.Delete, out var deletedCount));
Assert.Equal(3, deletedCount);
var update = history[1];
Assert.Equal(UpdateKind.Update, update.Kind);
Assert.Equal(UpdateState.Succeeded, update.Result);
Assert.NotNull(update.ResourceChanges);
Assert.Equal(1, update.ResourceChanges!.Count);
Assert.True(update.ResourceChanges.TryGetValue(OperationType.Create, out var createdCount));
Assert.Equal(3, createdCount);
}
}
}

View file

@ -0,0 +1,101 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Text.Json;
using Pulumi.Automation.Serialization;
using Xunit;
namespace Pulumi.Automation.Tests.Serialization
{
public class ProjectRuntimeJsonConverterTests
{
private static LocalSerializer _serializer = new LocalSerializer();
[Theory]
[InlineData(ProjectRuntimeName.NodeJS)]
[InlineData(ProjectRuntimeName.Go)]
[InlineData(ProjectRuntimeName.Python)]
[InlineData(ProjectRuntimeName.Dotnet)]
public void CanDeserializeWithStringRuntime(ProjectRuntimeName runtimeName)
{
var json = $@"
{{
""name"": ""test-project"",
""runtime"": ""{runtimeName.ToString().ToLower()}""
}}
";
var settings = _serializer.DeserializeJson<ProjectSettings>(json);
Assert.NotNull(settings);
Assert.IsType<ProjectSettings>(settings);
Assert.Equal("test-project", settings.Name);
Assert.Equal(runtimeName, settings.Runtime.Name);
Assert.Null(settings.Runtime.Options);
}
[Theory]
[InlineData(ProjectRuntimeName.NodeJS)]
[InlineData(ProjectRuntimeName.Go)]
[InlineData(ProjectRuntimeName.Python)]
[InlineData(ProjectRuntimeName.Dotnet)]
public void CanDeserializeWithObjectRuntime(ProjectRuntimeName runtimeName)
{
var json = $@"
{{
""name"": ""test-project"",
""runtime"": {{
""name"": ""{runtimeName.ToString().ToLower()}"",
""options"": {{
""typeScript"": true,
""binary"": ""test-binary"",
""virtualEnv"": ""test-env""
}}
}}
}}
";
var settings = _serializer.DeserializeJson<ProjectSettings>(json);
Assert.NotNull(settings);
Assert.IsType<ProjectSettings>(settings);
Assert.Equal("test-project", settings.Name);
Assert.Equal(runtimeName, settings.Runtime.Name);
Assert.NotNull(settings.Runtime.Options);
Assert.Equal(true, settings.Runtime.Options!.TypeScript);
Assert.Equal("test-binary", settings.Runtime.Options.Binary);
Assert.Equal("test-env", settings.Runtime.Options.VirtualEnv);
}
[Fact]
public void SerializesAsStringIfOptionsNull()
{
var runtime = new ProjectRuntime(ProjectRuntimeName.Dotnet);
var json = _serializer.SerializeJson(runtime);
Console.WriteLine(json);
using var document = JsonDocument.Parse(json);
Assert.NotNull(document);
Assert.Equal(JsonValueKind.String, document.RootElement.ValueKind);
Assert.Equal("dotnet", document.RootElement.GetString());
}
[Fact]
public void SerializesAsObjectIfOptionsNotNull()
{
var runtime = new ProjectRuntime(ProjectRuntimeName.Dotnet)
{
Options = new ProjectRuntimeOptions
{
TypeScript = true,
},
};
var json = _serializer.SerializeJson(runtime);
Console.WriteLine(json);
using var document = JsonDocument.Parse(json);
Assert.NotNull(document);
Assert.Equal(JsonValueKind.Object, document.RootElement.ValueKind);
}
}
}

View file

@ -0,0 +1,97 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Text;
using Pulumi.Automation.Serialization;
using Xunit;
namespace Pulumi.Automation.Tests.Serialization
{
public class ProjectRuntimeYamlConverterTests
{
private static LocalSerializer _serializer = new LocalSerializer();
[Theory]
[InlineData(ProjectRuntimeName.NodeJS)]
[InlineData(ProjectRuntimeName.Go)]
[InlineData(ProjectRuntimeName.Python)]
[InlineData(ProjectRuntimeName.Dotnet)]
public void CanDeserializeWithStringRuntime(ProjectRuntimeName runtimeName)
{
var yaml = $@"
name: test-project
runtime: {runtimeName.ToString().ToLower()}
";
var model = _serializer.DeserializeYaml<ProjectSettingsModel>(yaml);
var settings = model.Convert();
Assert.NotNull(settings);
Assert.IsType<ProjectSettings>(settings);
Assert.Equal("test-project", settings.Name);
Assert.Equal(runtimeName, settings.Runtime.Name);
Assert.Null(settings.Runtime.Options);
}
[Theory]
[InlineData(ProjectRuntimeName.NodeJS)]
[InlineData(ProjectRuntimeName.Go)]
[InlineData(ProjectRuntimeName.Python)]
[InlineData(ProjectRuntimeName.Dotnet)]
public void CanDeserializeWithObjectRuntime(ProjectRuntimeName runtimeName)
{
var yaml = $@"
name: test-project
runtime:
name: {runtimeName.ToString().ToLower()}
options:
typescript: true
binary: test-binary
virtualenv: test-env
";
var model = _serializer.DeserializeYaml<ProjectSettingsModel>(yaml);
var settings = model.Convert();
Assert.NotNull(settings);
Assert.IsType<ProjectSettings>(settings);
Assert.Equal("test-project", settings.Name);
Assert.Equal(runtimeName, settings.Runtime.Name);
Assert.NotNull(settings.Runtime.Options);
Assert.Equal(true, settings.Runtime.Options!.TypeScript);
Assert.Equal("test-binary", settings.Runtime.Options.Binary);
Assert.Equal("test-env", settings.Runtime.Options.VirtualEnv);
}
[Fact]
public void SerializesAsStringIfOptionsNull()
{
var runtime = new ProjectRuntime(ProjectRuntimeName.Dotnet);
var yaml = _serializer.SerializeYaml(runtime);
Console.WriteLine(yaml);
Assert.Equal("dotnet\r\n", yaml);
}
[Fact]
public void SerializesAsObjectIfOptionsNotNull()
{
var runtime = new ProjectRuntime(ProjectRuntimeName.Dotnet)
{
Options = new ProjectRuntimeOptions
{
TypeScript = true,
},
};
var yaml = _serializer.SerializeYaml(runtime);
Console.WriteLine(yaml);
var expected = new StringBuilder();
expected.Append("name: dotnet\r\n");
expected.Append("options:\r\n");
expected.Append(" typescript: true\r\n");
Assert.Equal(expected.ToString(), yaml);
}
}
}

View file

@ -0,0 +1,104 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Text.Json;
using Pulumi.Automation.Serialization;
using Xunit;
namespace Pulumi.Automation.Tests.Serialization
{
public class StackSettingsConfigValueJsonConverterTests
{
private static LocalSerializer _serializer = new LocalSerializer();
[Fact]
public void CanDeserializePlainString()
{
const string json = @"
{
""config"": {
""test"": ""plain""
}
}
";
var settings = _serializer.DeserializeJson<StackSettings>(json);
Assert.NotNull(settings?.Config);
Assert.True(settings!.Config!.ContainsKey("test"));
var value = settings.Config["test"];
Assert.NotNull(value);
Assert.Equal("plain", value.Value);
Assert.False(value.IsSecure);
}
[Fact]
public void CanDeserializeSecureString()
{
const string json = @"
{
""config"": {
""test"": {
""secure"": ""secret""
}
}
}
";
var settings = _serializer.DeserializeJson<StackSettings>(json);
Assert.NotNull(settings?.Config);
Assert.True(settings!.Config!.ContainsKey("test"));
var value = settings.Config["test"];
Assert.NotNull(value);
Assert.Equal("secret", value.Value);
Assert.True(value.IsSecure);
}
[Fact]
public void CannotDeserializeObject()
{
const string json = @"
{
""config"": {
""value"": {
""test"": ""test"",
""nested"": {
""one"": 1,
""two"": true,
""three"": ""three""
}
}
}
}
";
Assert.Throws<NotSupportedException>(
() => _serializer.DeserializeJson<StackSettings>(json));
}
[Fact]
public void SerializesPlainStringAsString()
{
var value = new StackSettingsConfigValue("test", false);
var json = _serializer.SerializeJson(value);
var element = JsonSerializer.Deserialize<JsonElement>(json);
Assert.Equal(JsonValueKind.String, element.ValueKind);
Assert.Equal("test", element.GetString());
}
[Fact]
public void SerializesSecureStringAsObject()
{
var value = new StackSettingsConfigValue("secret", true);
var json = _serializer.SerializeJson(value);
var element = JsonSerializer.Deserialize<JsonElement>(json);
Assert.Equal(JsonValueKind.Object, element.ValueKind);
Assert.True(element.TryGetProperty("secure", out var secureProperty));
Assert.Equal(JsonValueKind.String, secureProperty.ValueKind);
Assert.Equal("secret", secureProperty.GetString());
}
}
}

View file

@ -0,0 +1,83 @@
// Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization;
using Xunit;
using YamlDotNet.Core;
namespace Pulumi.Automation.Tests.Serialization
{
public class StackSettingsConfigValueYamlConverterTests
{
private static LocalSerializer _serializer = new LocalSerializer();
[Fact]
public void CanDeserializePlainString()
{
const string yaml = @"
config:
test: plain
";
var settings = _serializer.DeserializeYaml<StackSettings>(yaml);
Assert.NotNull(settings?.Config);
Assert.True(settings!.Config!.ContainsKey("test"));
var value = settings.Config["test"];
Assert.NotNull(value);
Assert.Equal("plain", value.Value);
Assert.False(value.IsSecure);
}
[Fact]
public void CanDeserializeSecureString()
{
const string yaml = @"
config:
test:
secure: secret
";
var settings = _serializer.DeserializeYaml<StackSettings>(yaml);
Assert.NotNull(settings?.Config);
Assert.True(settings!.Config!.ContainsKey("test"));
var value = settings.Config["test"];
Assert.NotNull(value);
Assert.Equal("secret", value.Value);
Assert.True(value.IsSecure);
}
[Fact]
public void CannotDeserializeObject()
{
const string yaml = @"
config:
value:
test: test
nested:
one: 1
two: true
three: three
";
Assert.Throws<YamlException>(
() => _serializer.DeserializeYaml<StackSettings>(yaml));
}
[Fact]
public void SerializesPlainStringAsString()
{
var value = new StackSettingsConfigValue("test", false);
var yaml = _serializer.SerializeYaml(value);
Assert.Equal("test\r\n", yaml);
}
[Fact]
public void SerializesSecureStringAsObject()
{
var value = new StackSettingsConfigValue("secret", true);
var yaml = _serializer.SerializeYaml(value);
Assert.Equal("secure: secret\r\n", yaml);
}
}
}

View file

@ -0,0 +1,4 @@
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
"parallelizeTestCollections": false
}

View file

@ -0,0 +1,5 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Pulumi.Automation.Tests")]

View file

@ -0,0 +1,35 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Text;
namespace Pulumi.Automation.Commands
{
internal class CommandResult
{
public int Code { get; }
public string StandardOutput { get; }
public string StandardError { get; }
public CommandResult(
int code,
string standardOutput,
string standardError)
{
this.Code = code;
this.StandardOutput = standardOutput;
this.StandardError = standardError;
}
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"code: {this.Code}");
sb.AppendLine($"stdout: {this.StandardOutput}");
sb.AppendLine($"stderr: {this.StandardError}");
return sb.ToString();
}
}
}

View file

@ -0,0 +1,33 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Text.RegularExpressions;
namespace Pulumi.Automation.Commands.Exceptions
{
public class CommandException : Exception
{
public string Name { get; }
internal CommandException(CommandResult result)
: this(nameof(CommandException), result)
{
}
internal CommandException(string name, CommandResult result)
: base(result.ToString())
{
this.Name = name;
}
private static readonly Regex NotFoundRegexPattern = new Regex("no stack named.*found");
private static readonly Regex AlreadyExistsRegexPattern = new Regex("stack.*already exists");
private static readonly string ConflictText = "[409] Conflict: Another update is currently in progress.";
internal static CommandException CreateFromResult(CommandResult result)
=> NotFoundRegexPattern.IsMatch(result.StandardError) ? new StackNotFoundException(result)
: AlreadyExistsRegexPattern.IsMatch(result.StandardError) ? new StackAlreadyExistsException(result)
: result.StandardError?.IndexOf(ConflictText) >= 0 ? new ConcurrentUpdateException(result)
: new CommandException(result);
}
}

View file

@ -0,0 +1,12 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation.Commands.Exceptions
{
public sealed class ConcurrentUpdateException : CommandException
{
internal ConcurrentUpdateException(CommandResult result)
: base(nameof(ConcurrentUpdateException), result)
{
}
}
}

View file

@ -0,0 +1,12 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation.Commands.Exceptions
{
public class StackAlreadyExistsException : CommandException
{
internal StackAlreadyExistsException(CommandResult result)
: base(nameof(StackAlreadyExistsException), result)
{
}
}
}

View file

@ -0,0 +1,12 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation.Commands.Exceptions
{
public class StackNotFoundException : CommandException
{
internal StackNotFoundException(CommandResult result)
: base(nameof(StackNotFoundException), result)
{
}
}
}

View file

@ -0,0 +1,19 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Pulumi.Automation.Commands
{
internal interface IPulumiCmd
{
Task<CommandResult> RunAsync(
IEnumerable<string> args,
string workingDir,
IDictionary<string, string> additionalEnv,
Action<string>? onOutput = null,
CancellationToken cancellationToken = default);
}
}

View file

@ -0,0 +1,108 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pulumi.Automation.Commands.Exceptions;
namespace Pulumi.Automation.Commands
{
internal class LocalPulumiCmd : IPulumiCmd
{
public async Task<CommandResult> RunAsync(
IEnumerable<string> args,
string workingDir,
IDictionary<string, string> additionalEnv,
Action<string>? onOutput = null,
CancellationToken cancellationToken = default)
{
// all commands should be run in non-interactive mode.
// this causes commands to fail rather than prompting for input (and thus hanging indefinitely)
var completeArgs = args.Concat(new[] { "--non-interactive" });
var env = new Dictionary<string, string>();
foreach (var element in Environment.GetEnvironmentVariables())
{
if (element is KeyValuePair<string, object> pair
&& pair.Value is string valueStr)
env[pair.Key] = valueStr;
}
foreach (var pair in additionalEnv)
env[pair.Key] = pair.Value;
using var proc = new Process
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo
{
FileName = "pulumi",
WorkingDirectory = workingDir,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
},
};
foreach (var arg in completeArgs)
proc.StartInfo.ArgumentList.Add(arg);
foreach (var pair in env)
proc.StartInfo.Environment[pair.Key] = pair.Value;
proc.OutputDataReceived += (_, @event) =>
{
if (@event.Data != null)
onOutput?.Invoke(@event.Data);
};
var tcs = new TaskCompletionSource<CommandResult>();
using var cancelRegistration = cancellationToken.Register(() =>
{
// if the process has already exited than let's
// just let it set the result on the task
if (proc.HasExited || tcs.Task.IsCompleted)
return;
// setting it cancelled before killing so there
// isn't a race condition to the proc.Exited event
tcs.TrySetCanceled(cancellationToken);
try
{
proc.Kill();
}
catch
{
// in case the process hasn't started yet
// or has already terminated
}
});
proc.Exited += async (_, @event) =>
{
var code = proc.ExitCode;
var stdOut = await proc.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
var stdErr = await proc.StandardError.ReadToEndAsync().ConfigureAwait(false);
var result = new CommandResult(code, stdOut, stdErr);
if (code != 0)
{
var ex = CommandException.CreateFromResult(result);
tcs.TrySetException(ex);
}
else
{
tcs.TrySetResult(result);
}
};
proc.Start();
return await tcs.Task.ConfigureAwait(false);
}
}
}

View file

@ -0,0 +1,19 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public class ConfigValue
{
public string Value { get; set; }
public bool IsSecret { get; set; }
public ConfigValue(
string value,
bool isSecret = false)
{
this.Value = value;
this.IsSecret = isSecret;
}
}
}

View file

@ -0,0 +1,16 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
namespace Pulumi.Automation
{
/// <summary>
/// Options controlling the behavior of an <see cref="WorkspaceStack.DestroyAsync(DestroyOptions, System.Threading.CancellationToken)"/> operation.
/// </summary>
public sealed class DestroyOptions : UpdateOptions
{
public bool? TargetDependents { get; set; }
public Action<string>? OnOutput { get; set; }
}
}

View file

@ -0,0 +1,14 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
/// <summary>
/// Options controlling the behavior of a <see cref="WorkspaceStack.GetHistoryAsync(HistoryOptions, System.Threading.CancellationToken)"/> operation.
/// </summary>
public sealed class HistoryOptions
{
public int? Page { get; set; }
public int? PageSize { get; set; }
}
}

View file

@ -0,0 +1,19 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public class InlineProgramArgs : LocalWorkspaceOptions
{
public string StackName { get; }
public InlineProgramArgs(
string projectName,
string stackName,
PulumiFn program)
{
this.ProjectSettings = ProjectSettings.Default(projectName);
this.StackName = stackName;
this.Program = program;
}
}
}

View file

@ -0,0 +1,20 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
/// <summary>
/// Description of a stack backed by pre-existing local Pulumi CLI program.
/// </summary>
public class LocalProgramArgs : LocalWorkspaceOptions
{
public string StackName { get; }
public LocalProgramArgs(
string stackName,
string workDir)
{
this.StackName = stackName;
this.WorkDir = workDir;
}
}
}

View file

@ -0,0 +1,598 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Pulumi.Automation.Commands;
using Pulumi.Automation.Serialization;
namespace Pulumi.Automation
{
/// <summary>
/// LocalWorkspace is a default implementation of the Workspace interface.
/// <para/>
/// A Workspace is the execution context containing a single Pulumi project, a program,
/// and multiple stacks.Workspaces are used to manage the execution environment,
/// providing various utilities such as plugin installation, environment configuration
/// ($PULUMI_HOME), and creation, deletion, and listing of Stacks.
/// <para/>
/// LocalWorkspace relies on Pulumi.yaml and Pulumi.{stack}.yaml as the intermediate format
/// for Project and Stack settings.Modifying ProjectSettings will
/// alter the Workspace Pulumi.yaml file, and setting config on a Stack will modify the Pulumi.{stack}.yaml file.
/// This is identical to the behavior of Pulumi CLI driven workspaces.
/// <para/>
/// If not provided a working directory - causing LocalWorkspace to create a temp directory,
/// than the temp directory will be cleaned up on <see cref="Dispose"/>.
/// </summary>
public sealed class LocalWorkspace : Workspace
{
private readonly LocalSerializer _serializer = new LocalSerializer();
private readonly bool _ownsWorkingDir;
private readonly Task _readyTask;
/// <inheritdoc/>
public override string WorkDir { get; }
/// <inheritdoc/>
public override string? PulumiHome { get; }
/// <inheritdoc/>
public override string? SecretsProvider { get; }
/// <inheritdoc/>
public override PulumiFn? Program { get; set; }
/// <inheritdoc/>
public override IDictionary<string, string>? EnvironmentVariables { get; set; }
/// <summary>
/// Creates a workspace using the specified options. Used for maximal control and
/// customization of the underlying environment before any stacks are created or selected.
/// </summary>
/// <param name="options">Options used to configure the workspace.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public static async Task<LocalWorkspace> CreateAsync(
LocalWorkspaceOptions? options = null,
CancellationToken cancellationToken = default)
{
var ws = new LocalWorkspace(
new LocalPulumiCmd(),
options,
cancellationToken);
await ws._readyTask.ConfigureAwait(false);
return ws;
}
/// <summary>
/// Creates a Stack with a <see cref="LocalWorkspace"/> utilizing the specified
/// inline (in process) <see cref="LocalWorkspaceOptions.Program"/>. This program
/// is fully debuggable and runs in process. If no <see cref="LocalWorkspaceOptions.ProjectSettings"/>
/// option is specified, default project settings will be created on behalf of the user. Similarly, unless a
/// <see cref="LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
/// to a new temporary directory provided by the OS.
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with an inline <see cref="PulumiFn"/> program
/// that runs in process, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
public static Task<WorkspaceStack> CreateStackAsync(InlineProgramArgs args)
=> CreateStackAsync(args, default);
/// <summary>
/// Creates a Stack with a <see cref="LocalWorkspace"/> utilizing the specified
/// inline (in process) <see cref="LocalWorkspaceOptions.Program"/>. This program
/// is fully debuggable and runs in process. If no <see cref="LocalWorkspaceOptions.ProjectSettings"/>
/// option is specified, default project settings will be created on behalf of the user. Similarly, unless a
/// <see cref="LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
/// to a new temporary directory provided by the OS.
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with an inline <see cref="PulumiFn"/> program
/// that runs in process, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
/// <param name="cancellationToken">A cancellation token.</param>
public static Task<WorkspaceStack> CreateStackAsync(InlineProgramArgs args, CancellationToken cancellationToken)
=> CreateStackHelperAsync(args, WorkspaceStack.CreateAsync, cancellationToken);
/// <summary>
/// Creates a Stack with a <see cref="LocalWorkspace"/> utilizing the local Pulumi CLI program
/// from the specified <see cref="LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
/// on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
/// files(Pulumi.yaml, Pulumi.{stack}.yaml).
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
/// already exists on disk, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
public static Task<WorkspaceStack> CreateStackAsync(LocalProgramArgs args)
=> CreateStackAsync(args, default);
/// <summary>
/// Creates a Stack with a <see cref="LocalWorkspace"/> utilizing the local Pulumi CLI program
/// from the specified <see cref="LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
/// on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
/// files(Pulumi.yaml, Pulumi.{stack}.yaml).
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
/// already exists on disk, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
/// <param name="cancellationToken">A cancellation token.</param>
public static Task<WorkspaceStack> CreateStackAsync(LocalProgramArgs args, CancellationToken cancellationToken)
=> CreateStackHelperAsync(args, WorkspaceStack.CreateAsync, cancellationToken);
/// <summary>
/// Selects an existing Stack with a <see cref="LocalWorkspace"/> utilizing the specified
/// inline (in process) <see cref="LocalWorkspaceOptions.Program"/>. This program
/// is fully debuggable and runs in process. If no <see cref="LocalWorkspaceOptions.ProjectSettings"/>
/// option is specified, default project settings will be created on behalf of the user. Similarly, unless a
/// <see cref="LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
/// to a new temporary directory provided by the OS.
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with an inline <see cref="PulumiFn"/> program
/// that runs in process, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
public static Task<WorkspaceStack> SelectStackAsync(InlineProgramArgs args)
=> SelectStackAsync(args, default);
/// <summary>
/// Selects an existing Stack with a <see cref="LocalWorkspace"/> utilizing the specified
/// inline (in process) <see cref="LocalWorkspaceOptions.Program"/>. This program
/// is fully debuggable and runs in process. If no <see cref="LocalWorkspaceOptions.ProjectSettings"/>
/// option is specified, default project settings will be created on behalf of the user. Similarly, unless a
/// <see cref="LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
/// to a new temporary directory provided by the OS.
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with an inline <see cref="PulumiFn"/> program
/// that runs in process, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
/// <param name="cancellationToken">A cancellation token.</param>
public static Task<WorkspaceStack> SelectStackAsync(InlineProgramArgs args, CancellationToken cancellationToken)
=> CreateStackHelperAsync(args, WorkspaceStack.SelectAsync, cancellationToken);
/// <summary>
/// Selects an existing Stack with a <see cref="LocalWorkspace"/> utilizing the local Pulumi CLI program
/// from the specified <see cref="LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
/// on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
/// files(Pulumi.yaml, Pulumi.{stack}.yaml).
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
/// already exists on disk, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
public static Task<WorkspaceStack> SelectStackAsync(LocalProgramArgs args)
=> SelectStackAsync(args, default);
/// <summary>
/// Selects an existing Stack with a <see cref="LocalWorkspace"/> utilizing the local Pulumi CLI program
/// from the specified <see cref="LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
/// on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
/// files(Pulumi.yaml, Pulumi.{stack}.yaml).
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
/// already exists on disk, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
/// <param name="cancellationToken">A cancellation token.</param>
public static Task<WorkspaceStack> SelectStackAsync(LocalProgramArgs args, CancellationToken cancellationToken)
=> CreateStackHelperAsync(args, WorkspaceStack.SelectAsync, cancellationToken);
/// <summary>
/// Creates or selects an existing Stack with a <see cref="LocalWorkspace"/> utilizing the specified
/// inline (in process) <see cref="LocalWorkspaceOptions.Program"/>. This program
/// is fully debuggable and runs in process. If no <see cref="LocalWorkspaceOptions.ProjectSettings"/>
/// option is specified, default project settings will be created on behalf of the user. Similarly, unless a
/// <see cref="LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
/// to a new temporary directory provided by the OS.
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with an inline <see cref="PulumiFn"/> program
/// that runs in process, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
public static Task<WorkspaceStack> CreateOrSelectStackAsync(InlineProgramArgs args)
=> CreateOrSelectStackAsync(args, default);
/// <summary>
/// Creates or selects an existing Stack with a <see cref="LocalWorkspace"/> utilizing the specified
/// inline (in process) <see cref="LocalWorkspaceOptions.Program"/>. This program
/// is fully debuggable and runs in process. If no <see cref="LocalWorkspaceOptions.ProjectSettings"/>
/// option is specified, default project settings will be created on behalf of the user. Similarly, unless a
/// <see cref="LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
/// to a new temporary directory provided by the OS.
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with an inline <see cref="PulumiFn"/> program
/// that runs in process, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
/// <param name="cancellationToken">A cancellation token.</param>
public static Task<WorkspaceStack> CreateOrSelectStackAsync(InlineProgramArgs args, CancellationToken cancellationToken)
=> CreateStackHelperAsync(args, WorkspaceStack.CreateOrSelectAsync, cancellationToken);
/// <summary>
/// Creates or selects an existing Stack with a <see cref="LocalWorkspace"/> utilizing the local Pulumi CLI program
/// from the specified <see cref="LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
/// on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
/// files(Pulumi.yaml, Pulumi.{stack}.yaml).
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
/// already exists on disk, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
public static Task<WorkspaceStack> CreateOrSelectStackAsync(LocalProgramArgs args)
=> CreateOrSelectStackAsync(args, default);
/// <summary>
/// Creates or selects an existing Stack with a <see cref="LocalWorkspace"/> utilizing the local Pulumi CLI program
/// from the specified <see cref="LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
/// on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
/// files(Pulumi.yaml, Pulumi.{stack}.yaml).
/// </summary>
/// <param name="args">
/// A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
/// already exists on disk, as well as any additional customizations to be applied to the
/// workspace.
/// </param>
/// <param name="cancellationToken">A cancellation token.</param>
public static Task<WorkspaceStack> CreateOrSelectStackAsync(LocalProgramArgs args, CancellationToken cancellationToken)
=> CreateStackHelperAsync(args, WorkspaceStack.CreateOrSelectAsync, cancellationToken);
private static async Task<WorkspaceStack> CreateStackHelperAsync(
InlineProgramArgs args,
Func<string, Workspace, CancellationToken, Task<WorkspaceStack>> initFunc,
CancellationToken cancellationToken)
{
if (args.ProjectSettings is null)
throw new ArgumentNullException(nameof(args.ProjectSettings));
var ws = new LocalWorkspace(
new LocalPulumiCmd(),
args,
cancellationToken);
await ws._readyTask.ConfigureAwait(false);
return await initFunc(args.StackName, ws, cancellationToken).ConfigureAwait(false);
}
private static async Task<WorkspaceStack> CreateStackHelperAsync(
LocalProgramArgs args,
Func<string, Workspace, CancellationToken, Task<WorkspaceStack>> initFunc,
CancellationToken cancellationToken)
{
var ws = new LocalWorkspace(
new LocalPulumiCmd(),
args,
cancellationToken);
await ws._readyTask.ConfigureAwait(false);
return await initFunc(args.StackName, ws, cancellationToken).ConfigureAwait(false);
}
internal LocalWorkspace(
IPulumiCmd cmd,
LocalWorkspaceOptions? options,
CancellationToken cancellationToken)
: base(cmd)
{
string? dir = null;
var readyTasks = new List<Task>();
if (options != null)
{
if (!string.IsNullOrWhiteSpace(options.WorkDir))
dir = options.WorkDir;
this.PulumiHome = options.PulumiHome;
this.Program = options.Program;
this.SecretsProvider = options.SecretsProvider;
if (options.EnvironmentVariables != null)
this.EnvironmentVariables = new Dictionary<string, string>(options.EnvironmentVariables);
}
if (string.IsNullOrWhiteSpace(dir))
{
// note that csharp doesn't guarantee that Path.GetRandomFileName returns a name
// for a file or folder that doesn't already exist.
// we should be OK with the "automation-" prefix but a collision is still
// theoretically possible
dir = Path.Combine(Path.GetTempPath(), $"automation-{Path.GetRandomFileName()}");
Directory.CreateDirectory(dir);
this._ownsWorkingDir = true;
}
this.WorkDir = dir;
// these are after working dir is set because they start immediately
if (options?.ProjectSettings != null)
readyTasks.Add(this.SaveProjectSettingsAsync(options.ProjectSettings, cancellationToken));
if (options?.StackSettings != null && options.StackSettings.Any())
{
foreach (var pair in options.StackSettings)
readyTasks.Add(this.SaveStackSettingsAsync(pair.Key, pair.Value, cancellationToken));
}
this._readyTask = Task.WhenAll(readyTasks);
}
private static readonly string[] SettingsExtensions = new string[] { ".yaml", ".yml", ".json" };
/// <inheritdoc/>
public override async Task<ProjectSettings?> GetProjectSettingsAsync(CancellationToken cancellationToken = default)
{
foreach (var ext in SettingsExtensions)
{
var isJson = ext == ".json";
var path = Path.Combine(this.WorkDir, $"Pulumi{ext}");
if (!File.Exists(path))
continue;
var content = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false);
if (isJson)
return this._serializer.DeserializeJson<ProjectSettings>(content);
var model = this._serializer.DeserializeYaml<ProjectSettingsModel>(content);
return model.Convert();
}
return null;
}
/// <inheritdoc/>
public override Task SaveProjectSettingsAsync(ProjectSettings settings, CancellationToken cancellationToken = default)
{
var foundExt = ".yaml";
foreach (var ext in SettingsExtensions)
{
var testPath = Path.Combine(this.WorkDir, $"Pulumi{ext}");
if (File.Exists(testPath))
{
foundExt = ext;
break;
}
}
var path = Path.Combine(this.WorkDir, $"Pulumi{foundExt}");
var content = foundExt == ".json" ? this._serializer.SerializeJson(settings) : this._serializer.SerializeYaml(settings);
return File.WriteAllTextAsync(path, content, cancellationToken);
}
private static string GetStackSettingsName(string stackName)
{
var parts = stackName.Split('/');
if (parts.Length < 1)
return stackName;
return parts[^1];
}
/// <inheritdoc/>
public override async Task<StackSettings?> GetStackSettingsAsync(string stackName, CancellationToken cancellationToken = default)
{
var settingsName = GetStackSettingsName(stackName);
foreach (var ext in SettingsExtensions)
{
var isJson = ext == ".json";
var path = Path.Combine(this.WorkDir, $"Pulumi.{settingsName}{ext}");
if (!File.Exists(path))
continue;
var content = await File.ReadAllTextAsync(path, cancellationToken).ConfigureAwait(false);
return isJson ? this._serializer.DeserializeJson<StackSettings>(content) : this._serializer.DeserializeYaml<StackSettings>(content);
}
return null;
}
/// <inheritdoc/>
public override Task SaveStackSettingsAsync(string stackName, StackSettings settings, CancellationToken cancellationToken = default)
{
var settingsName = GetStackSettingsName(stackName);
var foundExt = ".yaml";
foreach (var ext in SettingsExtensions)
{
var testPath = Path.Combine(this.WorkDir, $"Pulumi.{settingsName}{ext}");
if (File.Exists(testPath))
{
foundExt = ext;
break;
}
}
var path = Path.Combine(this.WorkDir, $"Pulumi.{settingsName}{foundExt}");
var content = foundExt == ".json" ? this._serializer.SerializeJson(settings) : this._serializer.SerializeYaml(settings);
return File.WriteAllTextAsync(path, content, cancellationToken);
}
/// <inheritdoc/>
public override Task<ImmutableList<string>> SerializeArgsForOpAsync(string stackName, CancellationToken cancellationToken = default)
=> Task.FromResult(ImmutableList<string>.Empty);
/// <inheritdoc/>
public override Task PostCommandCallbackAsync(string stackName, CancellationToken cancellationToken = default)
=> Task.CompletedTask;
/// <inheritdoc/>
public override async Task<ConfigValue> GetConfigValueAsync(string stackName, string key, CancellationToken cancellationToken = default)
{
await this.SelectStackAsync(stackName, cancellationToken).ConfigureAwait(false);
var result = await this.RunCommandAsync(new[] { "config", "get", key, "--json" }, cancellationToken).ConfigureAwait(false);
return JsonSerializer.Deserialize<ConfigValue>(result.StandardOutput);
}
/// <inheritdoc/>
public override async Task<ImmutableDictionary<string, ConfigValue>> GetConfigAsync(string stackName, CancellationToken cancellationToken = default)
{
await this.SelectStackAsync(stackName, cancellationToken).ConfigureAwait(false);
return await this.GetConfigAsync(cancellationToken).ConfigureAwait(false);
}
private async Task<ImmutableDictionary<string, ConfigValue>> GetConfigAsync(CancellationToken cancellationToken)
{
var result = await this.RunCommandAsync(new[] { "config", "--show-secrets", "--json" }, cancellationToken).ConfigureAwait(false);
var dict = this._serializer.DeserializeJson<Dictionary<string, ConfigValue>>(result.StandardOutput);
return dict.ToImmutableDictionary();
}
/// <inheritdoc/>
public override async Task SetConfigValueAsync(string stackName, string key, ConfigValue value, CancellationToken cancellationToken = default)
{
await this.SelectStackAsync(stackName, cancellationToken).ConfigureAwait(false);
await this.SetConfigValueAsync(key, value, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc/>
public override async Task SetConfigAsync(string stackName, IDictionary<string, ConfigValue> configMap, CancellationToken cancellationToken = default)
{
// TODO: do this in parallel after this is fixed https://github.com/pulumi/pulumi/issues/3877
await this.SelectStackAsync(stackName, cancellationToken).ConfigureAwait(false);
foreach (var (key, value) in configMap)
await this.SetConfigValueAsync(key, value, cancellationToken).ConfigureAwait(false);
}
private async Task SetConfigValueAsync(string key, ConfigValue value, CancellationToken cancellationToken)
{
var secretArg = value.IsSecret ? "--secret" : "--plaintext";
await this.RunCommandAsync(new[] { "config", "set", key, value.Value, secretArg }, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc/>
public override async Task RemoveConfigValueAsync(string stackName, string key, CancellationToken cancellationToken = default)
{
await this.SelectStackAsync(stackName, cancellationToken).ConfigureAwait(false);
await this.RunCommandAsync(new[] { "config", "rm", key }, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc/>
public override async Task RemoveConfigAsync(string stackName, IEnumerable<string> keys, CancellationToken cancellationToken = default)
{
// TODO: do this in parallel after this is fixed https://github.com/pulumi/pulumi/issues/3877
await this.SelectStackAsync(stackName, cancellationToken).ConfigureAwait(false);
foreach (var key in keys)
await this.RunCommandAsync(new[] { "config", "rm", key }, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc/>
public override async Task<ImmutableDictionary<string, ConfigValue>> RefreshConfigAsync(string stackName, CancellationToken cancellationToken = default)
{
await this.SelectStackAsync(stackName, cancellationToken).ConfigureAwait(false);
await this.RunCommandAsync(new[] { "config", "refresh", "--force" }, cancellationToken).ConfigureAwait(false);
return await this.GetConfigAsync(cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc/>
public override async Task<WhoAmIResult> WhoAmIAsync(CancellationToken cancellationToken = default)
{
var result = await this.RunCommandAsync(new[] { "whoami" }, cancellationToken).ConfigureAwait(false);
return new WhoAmIResult(result.StandardOutput.Trim());
}
/// <inheritdoc/>
public override Task CreateStackAsync(string stackName, CancellationToken cancellationToken)
{
var args = new List<string>()
{
"stack",
"init",
stackName,
};
if (!string.IsNullOrWhiteSpace(this.SecretsProvider))
args.AddRange(new[] { "--secrets-provider", this.SecretsProvider });
return this.RunCommandAsync(args, cancellationToken);
}
/// <inheritdoc/>
public override Task SelectStackAsync(string stackName, CancellationToken cancellationToken)
=> this.RunCommandAsync(new[] { "stack", "select", stackName }, cancellationToken);
/// <inheritdoc/>
public override Task RemoveStackAsync(string stackName, CancellationToken cancellationToken = default)
=> this.RunCommandAsync(new[] { "stack", "rm", "--yes", stackName }, cancellationToken);
/// <inheritdoc/>
public override async Task<ImmutableList<StackSummary>> ListStacksAsync(CancellationToken cancellationToken = default)
{
var result = await this.RunCommandAsync(new[] { "stack", "ls", "--json" }, cancellationToken).ConfigureAwait(false);
var stacks = this._serializer.DeserializeJson<List<StackSummary>>(result.StandardOutput);
return stacks.ToImmutableList();
}
/// <inheritdoc/>
public override Task InstallPluginAsync(string name, string version, PluginKind kind = PluginKind.Resource, CancellationToken cancellationToken = default)
=> this.RunCommandAsync(new[] { "plugin", "install", kind.ToString().ToLower(), name, version }, cancellationToken);
/// <inheritdoc/>
public override Task RemovePluginAsync(string? name = null, string? versionRange = null, PluginKind kind = PluginKind.Resource, CancellationToken cancellationToken = default)
{
var args = new List<string>()
{
"plugin",
"rm",
kind.ToString().ToLower(),
};
if (!string.IsNullOrWhiteSpace(name))
args.Add(name);
if (!string.IsNullOrWhiteSpace(versionRange))
args.Add(versionRange);
args.Add("--yes");
return this.RunCommandAsync(args, cancellationToken);
}
/// <inheritdoc/>
public override async Task<ImmutableList<PluginInfo>> ListPluginsAsync(CancellationToken cancellationToken = default)
{
var result = await this.RunCommandAsync(new[] { "plugin", "ls", "--json" }, cancellationToken).ConfigureAwait(false);
var plugins = this._serializer.DeserializeJson<List<PluginInfo>>(result.StandardOutput);
return plugins.ToImmutableList();
}
public override void Dispose()
{
base.Dispose();
if (this._ownsWorkingDir
&& !string.IsNullOrWhiteSpace(this.WorkDir)
&& Directory.Exists(this.WorkDir))
{
try
{
Directory.Delete(this.WorkDir, true);
}
catch
{
// 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.
}
}
}
}
}

View file

@ -0,0 +1,61 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic;
namespace Pulumi.Automation
{
/// <summary>
/// Extensibility options to configure a LocalWorkspace; e.g: settings to seed
/// and environment variables to pass through to every command.
/// </summary>
public class LocalWorkspaceOptions
{
/// <summary>
/// The directory to run Pulumi commands and read settings (Pulumi.yaml and Pulumi.{stack}.yaml).
/// </summary>
public string? WorkDir { get; set; }
/// <summary>
/// The directory to override for CLI metadata.
/// </summary>
public string? PulumiHome { get; set; }
/// <summary>
/// The secrets provider to user for encryption and decryption of stack secrets.
/// <para/>
/// See: https://www.pulumi.com/docs/intro/concepts/config/#available-encryption-providers
/// </summary>
public string? SecretsProvider { get; set; }
/// <summary>
/// The inline program <see cref="PulumiFn"/> to be used for Preview/Update operations if any.
/// <para/>
/// If none is specified, the stack will refer to <see cref="Automation.ProjectSettings"/> for this information.
/// </summary>
public PulumiFn? Program { get; set; }
/// <summary>
/// Environment values scoped to the current workspace. These will be supplied to every
/// Pulumi command.
/// </summary>
public IDictionary<string, string>? EnvironmentVariables { get; set; }
/// <summary>
/// The settings object for the current project.
/// <para/>
/// If provided when initializing <see cref="LocalWorkspace"/> a project settings
/// file will be written to when the workspace is initialized via
/// <see cref="LocalWorkspace.SaveProjectSettingsAsync(Automation.ProjectSettings, System.Threading.CancellationToken)"/>.
/// </summary>
public ProjectSettings? ProjectSettings { get; set; }
/// <summary>
/// A map of Stack names and corresponding settings objects.
/// <para/>
/// If provided when initializing <see cref="LocalWorkspace"/> stack settings
/// file(s) will be written to when the workspace is initialized via
/// <see cref="LocalWorkspace.SaveStackSettingsAsync(string, Automation.StackSettings, System.Threading.CancellationToken)"/>.
/// </summary>
public IDictionary<string, StackSettings>? StackSettings { get; set; }
}
}

View file

@ -0,0 +1,15 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public enum OperationType
{
Same,
Create,
Update,
Delete,
Replace,
CreateReplacement,
DeleteReplaced,
}
}

View file

@ -0,0 +1,19 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public sealed class OutputValue
{
public object Value { get; }
public bool IsSecret { get; }
internal OutputValue(
object value,
bool isSecret)
{
this.Value = value;
this.IsSecret = isSecret;
}
}
}

View file

@ -0,0 +1,45 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
namespace Pulumi.Automation
{
public class PluginInfo
{
public string Name { get; }
public string? Path { get; }
public PluginKind Kind { get; }
public string? Version { get; }
public long Size { get; }
public DateTimeOffset InstallTime { get; }
public DateTimeOffset LastUsedTime { get; }
public string? ServerUrl { get; }
internal PluginInfo(
string name,
string? path,
PluginKind kind,
string? version,
long size,
DateTimeOffset installTime,
DateTimeOffset lastUsedTime,
string? serverUrl)
{
this.Name = name;
this.Path = path;
this.Kind = kind;
this.Version = version;
this.Size = size;
this.InstallTime = installTime;
this.LastUsedTime = lastUsedTime;
this.ServerUrl = serverUrl;
}
}
}

View file

@ -0,0 +1,11 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public enum PluginKind
{
Analyzer,
Language,
Resource,
}
}

View file

@ -0,0 +1,20 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic;
namespace Pulumi.Automation
{
/// <summary>
/// Options controlling the behavior of an <see cref="WorkspaceStack.PreviewAsync(PreviewOptions, System.Threading.CancellationToken)"/> operation.
/// </summary>
public sealed class PreviewOptions : UpdateOptions
{
public bool? ExpectNoChanges { get; set; }
public List<string>? Replace { get; set; }
public bool? TargetDependents { get; set; }
public PulumiFn? Program { get; set; }
}
}

View file

@ -0,0 +1,12 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
/// <summary>
/// Configuration for the project's Pulumi state storage backend.
/// </summary>
public class ProjectBackend
{
public string? Url { get; set; }
}
}

View file

@ -0,0 +1,19 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
/// <summary>
/// A description of the Project's program runtime and associated metadata.
/// </summary>
public class ProjectRuntime
{
public ProjectRuntimeName Name { get; set; }
public ProjectRuntimeOptions? Options { get; set; }
public ProjectRuntime(ProjectRuntimeName name)
{
this.Name = name;
}
}
}

View file

@ -0,0 +1,15 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
/// <summary>
/// Supported Pulumi program language runtimes.
/// </summary>
public enum ProjectRuntimeName
{
NodeJS,
Go,
Python,
Dotnet,
}
}

View file

@ -0,0 +1,33 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
/// <summary>
/// Various configuration options that apply to different language runtimes.
/// </summary>
public class ProjectRuntimeOptions
{
/// <summary>
/// Applies to NodeJS projects only.
/// <para/>
/// A boolean that controls whether to use ts-node to execute sources.
/// </summary>
public bool? TypeScript { get; set; }
/// <summary>
/// Applies to Go and .NET project only.
/// <para/>
/// Go: A string that specifies the name of a pre-build executable to look for on your path.
/// <para/>
/// .NET: A string that specifies the path of a pre-build .NET assembly.
/// </summary>
public string? Binary { get; set; }
/// <summary>
/// Applies to Python projects only.
/// <para/>
/// A string that specifies the path to a virtual environment to use when running the program.
/// </summary>
public string? VirtualEnv { get; set; }
}
}

View file

@ -0,0 +1,48 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
/// <summary>
/// A Pulumi project manifest. It describes metadata applying to all sub-stacks created from the project.
/// </summary>
public class ProjectSettings
{
public string Name { get; set; }
public ProjectRuntime Runtime { get; set; }
public string? Main { get; set; }
public string? Description { get; set; }
public string? Author { get; set; }
public string? Website { get; set; }
public string? License { get; set; }
public string? Config { get; set; }
public ProjectTemplate? Template { get; set; }
public ProjectBackend? Backend { get; set; }
public ProjectSettings(
string name,
ProjectRuntime runtime)
{
this.Name = name;
this.Runtime = runtime;
}
public ProjectSettings(
string name,
ProjectRuntimeName runtime)
: this(name, new ProjectRuntime(runtime))
{
}
internal static ProjectSettings Default(string name)
=> new ProjectSettings(name, new ProjectRuntime(ProjectRuntimeName.NodeJS));
}
}

View file

@ -0,0 +1,20 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic;
namespace Pulumi.Automation
{
/// <summary>
/// A template used to seed new stacks created from this project.
/// </summary>
public class ProjectTemplate
{
public string? Description { get; set; }
public string? QuickStart { get; set; }
public IDictionary<string, ProjectTemplateConfigValue>? Config { get; set; }
public bool? Important { get; set; }
}
}

View file

@ -0,0 +1,16 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
/// <summary>
/// A placeholder config value for a project template.
/// </summary>
public class ProjectTemplateConfigValue
{
public string? Description { get; set; }
public string? Default { get; set; }
public bool? Secret { get; set; }
}
}

View file

@ -0,0 +1 @@


View file

@ -0,0 +1,327 @@
Pulumi.Automation.Commands.Exceptions.CommandException
Pulumi.Automation.Commands.Exceptions.CommandException.Name.get -> string
Pulumi.Automation.Commands.Exceptions.ConcurrentUpdateException
Pulumi.Automation.Commands.Exceptions.StackAlreadyExistsException
Pulumi.Automation.Commands.Exceptions.StackNotFoundException
Pulumi.Automation.ConfigValue
Pulumi.Automation.ConfigValue.ConfigValue(string value, bool isSecret = false) -> void
Pulumi.Automation.ConfigValue.IsSecret.get -> bool
Pulumi.Automation.ConfigValue.IsSecret.set -> void
Pulumi.Automation.ConfigValue.Value.get -> string
Pulumi.Automation.ConfigValue.Value.set -> void
Pulumi.Automation.DestroyOptions
Pulumi.Automation.DestroyOptions.DestroyOptions() -> void
Pulumi.Automation.DestroyOptions.OnOutput.get -> System.Action<string>
Pulumi.Automation.DestroyOptions.OnOutput.set -> void
Pulumi.Automation.DestroyOptions.TargetDependents.get -> bool?
Pulumi.Automation.DestroyOptions.TargetDependents.set -> void
Pulumi.Automation.HistoryOptions
Pulumi.Automation.HistoryOptions.HistoryOptions() -> void
Pulumi.Automation.HistoryOptions.Page.get -> int?
Pulumi.Automation.HistoryOptions.Page.set -> void
Pulumi.Automation.HistoryOptions.PageSize.get -> int?
Pulumi.Automation.HistoryOptions.PageSize.set -> void
Pulumi.Automation.InlineProgramArgs
Pulumi.Automation.InlineProgramArgs.InlineProgramArgs(string projectName, string stackName, Pulumi.Automation.PulumiFn program) -> void
Pulumi.Automation.InlineProgramArgs.StackName.get -> string
Pulumi.Automation.LocalProgramArgs
Pulumi.Automation.LocalProgramArgs.LocalProgramArgs(string stackName, string workDir) -> void
Pulumi.Automation.LocalProgramArgs.StackName.get -> string
Pulumi.Automation.LocalWorkspace
Pulumi.Automation.LocalWorkspaceOptions
Pulumi.Automation.LocalWorkspaceOptions.EnvironmentVariables.get -> System.Collections.Generic.IDictionary<string, string>
Pulumi.Automation.LocalWorkspaceOptions.EnvironmentVariables.set -> void
Pulumi.Automation.LocalWorkspaceOptions.LocalWorkspaceOptions() -> void
Pulumi.Automation.LocalWorkspaceOptions.Program.get -> Pulumi.Automation.PulumiFn
Pulumi.Automation.LocalWorkspaceOptions.Program.set -> void
Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings.get -> Pulumi.Automation.ProjectSettings
Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings.set -> void
Pulumi.Automation.LocalWorkspaceOptions.PulumiHome.get -> string
Pulumi.Automation.LocalWorkspaceOptions.PulumiHome.set -> void
Pulumi.Automation.LocalWorkspaceOptions.SecretsProvider.get -> string
Pulumi.Automation.LocalWorkspaceOptions.SecretsProvider.set -> void
Pulumi.Automation.LocalWorkspaceOptions.StackSettings.get -> System.Collections.Generic.IDictionary<string, Pulumi.Automation.StackSettings>
Pulumi.Automation.LocalWorkspaceOptions.StackSettings.set -> void
Pulumi.Automation.LocalWorkspaceOptions.WorkDir.get -> string
Pulumi.Automation.LocalWorkspaceOptions.WorkDir.set -> void
Pulumi.Automation.OperationType
Pulumi.Automation.OperationType.Create = 1 -> Pulumi.Automation.OperationType
Pulumi.Automation.OperationType.CreateReplacement = 5 -> Pulumi.Automation.OperationType
Pulumi.Automation.OperationType.Delete = 3 -> Pulumi.Automation.OperationType
Pulumi.Automation.OperationType.DeleteReplaced = 6 -> Pulumi.Automation.OperationType
Pulumi.Automation.OperationType.Replace = 4 -> Pulumi.Automation.OperationType
Pulumi.Automation.OperationType.Same = 0 -> Pulumi.Automation.OperationType
Pulumi.Automation.OperationType.Update = 2 -> Pulumi.Automation.OperationType
Pulumi.Automation.OutputValue
Pulumi.Automation.OutputValue.IsSecret.get -> bool
Pulumi.Automation.OutputValue.Value.get -> object
Pulumi.Automation.PluginInfo
Pulumi.Automation.PluginInfo.InstallTime.get -> System.DateTimeOffset
Pulumi.Automation.PluginInfo.Kind.get -> Pulumi.Automation.PluginKind
Pulumi.Automation.PluginInfo.LastUsedTime.get -> System.DateTimeOffset
Pulumi.Automation.PluginInfo.Name.get -> string
Pulumi.Automation.PluginInfo.Path.get -> string
Pulumi.Automation.PluginInfo.ServerUrl.get -> string
Pulumi.Automation.PluginInfo.Size.get -> long
Pulumi.Automation.PluginInfo.Version.get -> string
Pulumi.Automation.PluginKind
Pulumi.Automation.PluginKind.Analyzer = 0 -> Pulumi.Automation.PluginKind
Pulumi.Automation.PluginKind.Language = 1 -> Pulumi.Automation.PluginKind
Pulumi.Automation.PluginKind.Resource = 2 -> Pulumi.Automation.PluginKind
Pulumi.Automation.PreviewOptions
Pulumi.Automation.PreviewOptions.ExpectNoChanges.get -> bool?
Pulumi.Automation.PreviewOptions.ExpectNoChanges.set -> void
Pulumi.Automation.PreviewOptions.PreviewOptions() -> void
Pulumi.Automation.PreviewOptions.Program.get -> Pulumi.Automation.PulumiFn
Pulumi.Automation.PreviewOptions.Program.set -> void
Pulumi.Automation.PreviewOptions.Replace.get -> System.Collections.Generic.List<string>
Pulumi.Automation.PreviewOptions.Replace.set -> void
Pulumi.Automation.PreviewOptions.TargetDependents.get -> bool?
Pulumi.Automation.PreviewOptions.TargetDependents.set -> void
Pulumi.Automation.ProjectBackend
Pulumi.Automation.ProjectBackend.ProjectBackend() -> void
Pulumi.Automation.ProjectBackend.Url.get -> string
Pulumi.Automation.ProjectBackend.Url.set -> void
Pulumi.Automation.ProjectRuntime
Pulumi.Automation.ProjectRuntime.Name.get -> Pulumi.Automation.ProjectRuntimeName
Pulumi.Automation.ProjectRuntime.Name.set -> void
Pulumi.Automation.ProjectRuntime.Options.get -> Pulumi.Automation.ProjectRuntimeOptions
Pulumi.Automation.ProjectRuntime.Options.set -> void
Pulumi.Automation.ProjectRuntime.ProjectRuntime(Pulumi.Automation.ProjectRuntimeName name) -> void
Pulumi.Automation.ProjectRuntimeName
Pulumi.Automation.ProjectRuntimeName.Dotnet = 3 -> Pulumi.Automation.ProjectRuntimeName
Pulumi.Automation.ProjectRuntimeName.Go = 1 -> Pulumi.Automation.ProjectRuntimeName
Pulumi.Automation.ProjectRuntimeName.NodeJS = 0 -> Pulumi.Automation.ProjectRuntimeName
Pulumi.Automation.ProjectRuntimeName.Python = 2 -> Pulumi.Automation.ProjectRuntimeName
Pulumi.Automation.ProjectRuntimeOptions
Pulumi.Automation.ProjectRuntimeOptions.Binary.get -> string
Pulumi.Automation.ProjectRuntimeOptions.Binary.set -> void
Pulumi.Automation.ProjectRuntimeOptions.ProjectRuntimeOptions() -> void
Pulumi.Automation.ProjectRuntimeOptions.TypeScript.get -> bool?
Pulumi.Automation.ProjectRuntimeOptions.TypeScript.set -> void
Pulumi.Automation.ProjectRuntimeOptions.VirtualEnv.get -> string
Pulumi.Automation.ProjectRuntimeOptions.VirtualEnv.set -> void
Pulumi.Automation.ProjectSettings
Pulumi.Automation.ProjectSettings.Author.get -> string
Pulumi.Automation.ProjectSettings.Author.set -> void
Pulumi.Automation.ProjectSettings.Backend.get -> Pulumi.Automation.ProjectBackend
Pulumi.Automation.ProjectSettings.Backend.set -> void
Pulumi.Automation.ProjectSettings.Config.get -> string
Pulumi.Automation.ProjectSettings.Config.set -> void
Pulumi.Automation.ProjectSettings.Description.get -> string
Pulumi.Automation.ProjectSettings.Description.set -> void
Pulumi.Automation.ProjectSettings.License.get -> string
Pulumi.Automation.ProjectSettings.License.set -> void
Pulumi.Automation.ProjectSettings.Main.get -> string
Pulumi.Automation.ProjectSettings.Main.set -> void
Pulumi.Automation.ProjectSettings.Name.get -> string
Pulumi.Automation.ProjectSettings.Name.set -> void
Pulumi.Automation.ProjectSettings.ProjectSettings(string name, Pulumi.Automation.ProjectRuntime runtime) -> void
Pulumi.Automation.ProjectSettings.ProjectSettings(string name, Pulumi.Automation.ProjectRuntimeName runtime) -> void
Pulumi.Automation.ProjectSettings.Runtime.get -> Pulumi.Automation.ProjectRuntime
Pulumi.Automation.ProjectSettings.Runtime.set -> void
Pulumi.Automation.ProjectSettings.Template.get -> Pulumi.Automation.ProjectTemplate
Pulumi.Automation.ProjectSettings.Template.set -> void
Pulumi.Automation.ProjectSettings.Website.get -> string
Pulumi.Automation.ProjectSettings.Website.set -> void
Pulumi.Automation.ProjectTemplate
Pulumi.Automation.ProjectTemplate.Config.get -> System.Collections.Generic.IDictionary<string, Pulumi.Automation.ProjectTemplateConfigValue>
Pulumi.Automation.ProjectTemplate.Config.set -> void
Pulumi.Automation.ProjectTemplate.Description.get -> string
Pulumi.Automation.ProjectTemplate.Description.set -> void
Pulumi.Automation.ProjectTemplate.Important.get -> bool?
Pulumi.Automation.ProjectTemplate.Important.set -> void
Pulumi.Automation.ProjectTemplate.ProjectTemplate() -> void
Pulumi.Automation.ProjectTemplate.QuickStart.get -> string
Pulumi.Automation.ProjectTemplate.QuickStart.set -> void
Pulumi.Automation.ProjectTemplateConfigValue
Pulumi.Automation.ProjectTemplateConfigValue.Default.get -> string
Pulumi.Automation.ProjectTemplateConfigValue.Default.set -> void
Pulumi.Automation.ProjectTemplateConfigValue.Description.get -> string
Pulumi.Automation.ProjectTemplateConfigValue.Description.set -> void
Pulumi.Automation.ProjectTemplateConfigValue.ProjectTemplateConfigValue() -> void
Pulumi.Automation.ProjectTemplateConfigValue.Secret.get -> bool?
Pulumi.Automation.ProjectTemplateConfigValue.Secret.set -> void
Pulumi.Automation.PulumiFn
Pulumi.Automation.RefreshOptions
Pulumi.Automation.RefreshOptions.ExpectNoChanges.get -> bool?
Pulumi.Automation.RefreshOptions.ExpectNoChanges.set -> void
Pulumi.Automation.RefreshOptions.OnOutput.get -> System.Action<string>
Pulumi.Automation.RefreshOptions.OnOutput.set -> void
Pulumi.Automation.RefreshOptions.RefreshOptions() -> void
Pulumi.Automation.StackSettings
Pulumi.Automation.StackSettings.Config.get -> System.Collections.Generic.IDictionary<string, Pulumi.Automation.StackSettingsConfigValue>
Pulumi.Automation.StackSettings.Config.set -> void
Pulumi.Automation.StackSettings.EncryptedKey.get -> string
Pulumi.Automation.StackSettings.EncryptedKey.set -> void
Pulumi.Automation.StackSettings.EncryptionSalt.get -> string
Pulumi.Automation.StackSettings.EncryptionSalt.set -> void
Pulumi.Automation.StackSettings.SecretsProvider.get -> string
Pulumi.Automation.StackSettings.SecretsProvider.set -> void
Pulumi.Automation.StackSettings.StackSettings() -> void
Pulumi.Automation.StackSettingsConfigValue
Pulumi.Automation.StackSettingsConfigValue.IsSecure.get -> bool
Pulumi.Automation.StackSettingsConfigValue.StackSettingsConfigValue(string value, bool isSecure) -> void
Pulumi.Automation.StackSettingsConfigValue.Value.get -> string
Pulumi.Automation.StackSummary
Pulumi.Automation.StackSummary.IsCurrent.get -> bool
Pulumi.Automation.StackSummary.IsUpdateInProgress.get -> bool
Pulumi.Automation.StackSummary.LastUpdate.get -> System.DateTimeOffset?
Pulumi.Automation.StackSummary.Name.get -> string
Pulumi.Automation.StackSummary.ResourceCount.get -> int?
Pulumi.Automation.StackSummary.Url.get -> string
Pulumi.Automation.UpOptions
Pulumi.Automation.UpOptions.ExpectNoChanges.get -> bool?
Pulumi.Automation.UpOptions.ExpectNoChanges.set -> void
Pulumi.Automation.UpOptions.OnOutput.get -> System.Action<string>
Pulumi.Automation.UpOptions.OnOutput.set -> void
Pulumi.Automation.UpOptions.Program.get -> Pulumi.Automation.PulumiFn
Pulumi.Automation.UpOptions.Program.set -> void
Pulumi.Automation.UpOptions.Replace.get -> System.Collections.Generic.List<string>
Pulumi.Automation.UpOptions.Replace.set -> void
Pulumi.Automation.UpOptions.TargetDependents.get -> bool?
Pulumi.Automation.UpOptions.TargetDependents.set -> void
Pulumi.Automation.UpOptions.UpOptions() -> void
Pulumi.Automation.UpResult
Pulumi.Automation.UpResult.Outputs.get -> System.Collections.Immutable.IImmutableDictionary<string, Pulumi.Automation.OutputValue>
Pulumi.Automation.UpdateKind
Pulumi.Automation.UpdateKind.Destroy = 4 -> Pulumi.Automation.UpdateKind
Pulumi.Automation.UpdateKind.Import = 5 -> Pulumi.Automation.UpdateKind
Pulumi.Automation.UpdateKind.Preview = 1 -> Pulumi.Automation.UpdateKind
Pulumi.Automation.UpdateKind.Refresh = 2 -> Pulumi.Automation.UpdateKind
Pulumi.Automation.UpdateKind.Rename = 3 -> Pulumi.Automation.UpdateKind
Pulumi.Automation.UpdateKind.Update = 0 -> Pulumi.Automation.UpdateKind
Pulumi.Automation.UpdateOptions
Pulumi.Automation.UpdateOptions.Message.get -> string
Pulumi.Automation.UpdateOptions.Message.set -> void
Pulumi.Automation.UpdateOptions.Parallel.get -> int?
Pulumi.Automation.UpdateOptions.Parallel.set -> void
Pulumi.Automation.UpdateOptions.Target.get -> System.Collections.Generic.List<string>
Pulumi.Automation.UpdateOptions.Target.set -> void
Pulumi.Automation.UpdateOptions.UpdateOptions() -> void
Pulumi.Automation.UpdateResult
Pulumi.Automation.UpdateResult.StandardError.get -> string
Pulumi.Automation.UpdateResult.StandardOutput.get -> string
Pulumi.Automation.UpdateResult.Summary.get -> Pulumi.Automation.UpdateSummary
Pulumi.Automation.UpdateState
Pulumi.Automation.UpdateState.Failed = 3 -> Pulumi.Automation.UpdateState
Pulumi.Automation.UpdateState.InProgress = 1 -> Pulumi.Automation.UpdateState
Pulumi.Automation.UpdateState.NotStarted = 0 -> Pulumi.Automation.UpdateState
Pulumi.Automation.UpdateState.Succeeded = 2 -> Pulumi.Automation.UpdateState
Pulumi.Automation.UpdateSummary
Pulumi.Automation.UpdateSummary.Config.get -> System.Collections.Immutable.IImmutableDictionary<string, Pulumi.Automation.ConfigValue>
Pulumi.Automation.UpdateSummary.Deployment.get -> string
Pulumi.Automation.UpdateSummary.EndTime.get -> System.DateTimeOffset
Pulumi.Automation.UpdateSummary.Environment.get -> System.Collections.Immutable.IImmutableDictionary<string, string>
Pulumi.Automation.UpdateSummary.Kind.get -> Pulumi.Automation.UpdateKind
Pulumi.Automation.UpdateSummary.Message.get -> string
Pulumi.Automation.UpdateSummary.ResourceChanges.get -> System.Collections.Immutable.IImmutableDictionary<Pulumi.Automation.OperationType, int>
Pulumi.Automation.UpdateSummary.Result.get -> Pulumi.Automation.UpdateState
Pulumi.Automation.UpdateSummary.StartTime.get -> System.DateTimeOffset
Pulumi.Automation.UpdateSummary.Version.get -> int?
Pulumi.Automation.WhoAmIResult
Pulumi.Automation.WhoAmIResult.User.get -> string
Pulumi.Automation.WhoAmIResult.WhoAmIResult(string user) -> void
Pulumi.Automation.Workspace
Pulumi.Automation.Workspace.CreateStackAsync(string stackName) -> System.Threading.Tasks.Task
Pulumi.Automation.Workspace.SelectStackAsync(string stackName) -> System.Threading.Tasks.Task
Pulumi.Automation.WorkspaceStack
Pulumi.Automation.WorkspaceStack.DestroyAsync(Pulumi.Automation.DestroyOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.UpdateResult>
Pulumi.Automation.WorkspaceStack.Dispose() -> void
Pulumi.Automation.WorkspaceStack.GetConfigAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableDictionary<string, Pulumi.Automation.ConfigValue>>
Pulumi.Automation.WorkspaceStack.GetConfigValueAsync(string key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.ConfigValue>
Pulumi.Automation.WorkspaceStack.GetHistoryAsync(Pulumi.Automation.HistoryOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<Pulumi.Automation.UpdateSummary>>
Pulumi.Automation.WorkspaceStack.GetInfoAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.UpdateSummary>
Pulumi.Automation.WorkspaceStack.Name.get -> string
Pulumi.Automation.WorkspaceStack.PreviewAsync(Pulumi.Automation.PreviewOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.UpdateResult>
Pulumi.Automation.WorkspaceStack.RefreshAsync(Pulumi.Automation.RefreshOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.UpdateResult>
Pulumi.Automation.WorkspaceStack.RefreshConfigAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableDictionary<string, Pulumi.Automation.ConfigValue>>
Pulumi.Automation.WorkspaceStack.RemoveConfigAsync(System.Collections.Generic.IEnumerable<string> keys, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
Pulumi.Automation.WorkspaceStack.RemoveConfigValueAsync(string key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
Pulumi.Automation.WorkspaceStack.SetConfigAsync(System.Collections.Generic.IDictionary<string, Pulumi.Automation.ConfigValue> configMap, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
Pulumi.Automation.WorkspaceStack.SetConfigValueAsync(string key, Pulumi.Automation.ConfigValue value, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
Pulumi.Automation.WorkspaceStack.UpAsync(Pulumi.Automation.UpOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.UpResult>
Pulumi.Automation.WorkspaceStack.Workspace.get -> Pulumi.Automation.Workspace
abstract Pulumi.Automation.Workspace.CreateStackAsync(string stackName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.EnvironmentVariables.get -> System.Collections.Generic.IDictionary<string, string>
abstract Pulumi.Automation.Workspace.EnvironmentVariables.set -> void
abstract Pulumi.Automation.Workspace.GetConfigAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableDictionary<string, Pulumi.Automation.ConfigValue>>
abstract Pulumi.Automation.Workspace.GetConfigValueAsync(string stackName, string key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.ConfigValue>
abstract Pulumi.Automation.Workspace.GetProjectSettingsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.ProjectSettings>
abstract Pulumi.Automation.Workspace.GetStackSettingsAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.StackSettings>
abstract Pulumi.Automation.Workspace.InstallPluginAsync(string name, string version, Pulumi.Automation.PluginKind kind = Pulumi.Automation.PluginKind.Resource, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.ListPluginsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<Pulumi.Automation.PluginInfo>>
abstract Pulumi.Automation.Workspace.ListStacksAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<Pulumi.Automation.StackSummary>>
abstract Pulumi.Automation.Workspace.PostCommandCallbackAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.Program.get -> Pulumi.Automation.PulumiFn
abstract Pulumi.Automation.Workspace.Program.set -> void
abstract Pulumi.Automation.Workspace.PulumiHome.get -> string
abstract Pulumi.Automation.Workspace.RefreshConfigAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableDictionary<string, Pulumi.Automation.ConfigValue>>
abstract Pulumi.Automation.Workspace.RemoveConfigAsync(string stackName, System.Collections.Generic.IEnumerable<string> keys, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.RemoveConfigValueAsync(string stackName, string key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.RemovePluginAsync(string name = null, string versionRange = null, Pulumi.Automation.PluginKind kind = Pulumi.Automation.PluginKind.Resource, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.RemoveStackAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.SaveProjectSettingsAsync(Pulumi.Automation.ProjectSettings settings, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.SaveStackSettingsAsync(string stackName, Pulumi.Automation.StackSettings settings, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.SecretsProvider.get -> string
abstract Pulumi.Automation.Workspace.SelectStackAsync(string stackName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.SerializeArgsForOpAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<string>>
abstract Pulumi.Automation.Workspace.SetConfigAsync(string stackName, System.Collections.Generic.IDictionary<string, Pulumi.Automation.ConfigValue> configMap, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.SetConfigValueAsync(string stackName, string key, Pulumi.Automation.ConfigValue value, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
abstract Pulumi.Automation.Workspace.WhoAmIAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.WhoAmIResult>
abstract Pulumi.Automation.Workspace.WorkDir.get -> string
override Pulumi.Automation.LocalWorkspace.CreateStackAsync(string stackName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.Dispose() -> void
override Pulumi.Automation.LocalWorkspace.EnvironmentVariables.get -> System.Collections.Generic.IDictionary<string, string>
override Pulumi.Automation.LocalWorkspace.EnvironmentVariables.set -> void
override Pulumi.Automation.LocalWorkspace.GetConfigAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableDictionary<string, Pulumi.Automation.ConfigValue>>
override Pulumi.Automation.LocalWorkspace.GetConfigValueAsync(string stackName, string key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.ConfigValue>
override Pulumi.Automation.LocalWorkspace.GetProjectSettingsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.ProjectSettings>
override Pulumi.Automation.LocalWorkspace.GetStackSettingsAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.StackSettings>
override Pulumi.Automation.LocalWorkspace.InstallPluginAsync(string name, string version, Pulumi.Automation.PluginKind kind = Pulumi.Automation.PluginKind.Resource, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.ListPluginsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<Pulumi.Automation.PluginInfo>>
override Pulumi.Automation.LocalWorkspace.ListStacksAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<Pulumi.Automation.StackSummary>>
override Pulumi.Automation.LocalWorkspace.PostCommandCallbackAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.Program.get -> Pulumi.Automation.PulumiFn
override Pulumi.Automation.LocalWorkspace.Program.set -> void
override Pulumi.Automation.LocalWorkspace.PulumiHome.get -> string
override Pulumi.Automation.LocalWorkspace.RefreshConfigAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableDictionary<string, Pulumi.Automation.ConfigValue>>
override Pulumi.Automation.LocalWorkspace.RemoveConfigAsync(string stackName, System.Collections.Generic.IEnumerable<string> keys, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.RemoveConfigValueAsync(string stackName, string key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.RemovePluginAsync(string name = null, string versionRange = null, Pulumi.Automation.PluginKind kind = Pulumi.Automation.PluginKind.Resource, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.RemoveStackAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.SaveProjectSettingsAsync(Pulumi.Automation.ProjectSettings settings, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.SaveStackSettingsAsync(string stackName, Pulumi.Automation.StackSettings settings, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.SecretsProvider.get -> string
override Pulumi.Automation.LocalWorkspace.SelectStackAsync(string stackName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.SerializeArgsForOpAsync(string stackName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Immutable.ImmutableList<string>>
override Pulumi.Automation.LocalWorkspace.SetConfigAsync(string stackName, System.Collections.Generic.IDictionary<string, Pulumi.Automation.ConfigValue> configMap, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.SetConfigValueAsync(string stackName, string key, Pulumi.Automation.ConfigValue value, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
override Pulumi.Automation.LocalWorkspace.WhoAmIAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.WhoAmIResult>
override Pulumi.Automation.LocalWorkspace.WorkDir.get -> string
static Pulumi.Automation.LocalWorkspace.CreateAsync(Pulumi.Automation.LocalWorkspaceOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.LocalWorkspace>
static Pulumi.Automation.LocalWorkspace.CreateOrSelectStackAsync(Pulumi.Automation.InlineProgramArgs args) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.CreateOrSelectStackAsync(Pulumi.Automation.InlineProgramArgs args, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.CreateOrSelectStackAsync(Pulumi.Automation.LocalProgramArgs args) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.CreateOrSelectStackAsync(Pulumi.Automation.LocalProgramArgs args, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.CreateStackAsync(Pulumi.Automation.InlineProgramArgs args) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.CreateStackAsync(Pulumi.Automation.InlineProgramArgs args, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.CreateStackAsync(Pulumi.Automation.LocalProgramArgs args) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.CreateStackAsync(Pulumi.Automation.LocalProgramArgs args, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.SelectStackAsync(Pulumi.Automation.InlineProgramArgs args) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.SelectStackAsync(Pulumi.Automation.InlineProgramArgs args, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.SelectStackAsync(Pulumi.Automation.LocalProgramArgs args) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.LocalWorkspace.SelectStackAsync(Pulumi.Automation.LocalProgramArgs args, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.PulumiFn.Create(System.Action program) -> Pulumi.Automation.PulumiFn
static Pulumi.Automation.PulumiFn.Create(System.Func<System.Collections.Generic.IDictionary<string, object>> program) -> Pulumi.Automation.PulumiFn
static Pulumi.Automation.PulumiFn.Create(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<System.Collections.Generic.IDictionary<string, object>>> program) -> Pulumi.Automation.PulumiFn
static Pulumi.Automation.PulumiFn.Create(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> program) -> Pulumi.Automation.PulumiFn
static Pulumi.Automation.PulumiFn.Create(System.Func<System.Threading.Tasks.Task<System.Collections.Generic.IDictionary<string, object>>> program) -> Pulumi.Automation.PulumiFn
static Pulumi.Automation.PulumiFn.Create(System.Func<System.Threading.Tasks.Task> program) -> Pulumi.Automation.PulumiFn
static Pulumi.Automation.PulumiFn.Create<TStack>() -> Pulumi.Automation.PulumiFn
static Pulumi.Automation.PulumiFn.Create<TStack>(System.IServiceProvider serviceProvider) -> Pulumi.Automation.PulumiFn
static Pulumi.Automation.WorkspaceStack.CreateAsync(string name, Pulumi.Automation.Workspace workspace, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.WorkspaceStack.CreateOrSelectAsync(string name, Pulumi.Automation.Workspace workspace, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
static Pulumi.Automation.WorkspaceStack.SelectAsync(string name, Pulumi.Automation.Workspace workspace, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.WorkspaceStack>
virtual Pulumi.Automation.Workspace.Dispose() -> void
virtual Pulumi.Automation.Workspace.GetStackAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Pulumi.Automation.StackSummary>

View file

@ -0,0 +1,64 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <!-- true on release -->
<IsPackable>false</IsPackable> <!-- remove on release -->
<Authors>Pulumi</Authors>
<Company>Pulumi Corp.</Company>
<Description>Pulumi Automation API, the programmatic interface for driving Pulumi programs without the CLI.</Description>
<PackageProjectUrl>https://www.pulumi.com</PackageProjectUrl>
<RepositoryUrl>https://github.com/pulumi/pulumi</RepositoryUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageIcon>pulumi_logo_64x64.png</PackageIcon>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>.\Pulumi.Automation.xml</DocumentationFile>
<NoWarn>1701;1702;1591;NU5105</NoWarn>
</PropertyGroup>
<PropertyGroup>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="2.9.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Grpc.AspNetCore" Version="2.34.0" />
<PackageReference Include="YamlDotNet" Version="9.1.0" />
</ItemGroup>
<ItemGroup>
<None Remove="PublicAPI.Shipped.txt" />
<None Remove="PublicAPI.Unshipped.txt" />
<None Remove="Pulumi.Automation.xml" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="PublicAPI.Shipped.txt" />
<AdditionalFiles Include="PublicAPI.Unshipped.txt" />
</ItemGroup>
<ItemGroup>
<None Include="..\pulumi_logo_64x64.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Pulumi\Pulumi.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,881 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>Pulumi.Automation</name>
</assembly>
<members>
<member name="T:Pulumi.Automation.DestroyOptions">
<summary>
Options controlling the behavior of an <see cref="M:Pulumi.Automation.WorkspaceStack.DestroyAsync(Pulumi.Automation.DestroyOptions,System.Threading.CancellationToken)"/> operation.
</summary>
</member>
<member name="T:Pulumi.Automation.HistoryOptions">
<summary>
Options controlling the behavior of a <see cref="M:Pulumi.Automation.WorkspaceStack.GetHistoryAsync(Pulumi.Automation.HistoryOptions,System.Threading.CancellationToken)"/> operation.
</summary>
</member>
<member name="T:Pulumi.Automation.LocalProgramArgs">
<summary>
Description of a stack backed by pre-existing local Pulumi CLI program.
</summary>
</member>
<member name="T:Pulumi.Automation.LocalWorkspace">
<summary>
LocalWorkspace is a default implementation of the Workspace interface.
<para/>
A Workspace is the execution context containing a single Pulumi project, a program,
and multiple stacks.Workspaces are used to manage the execution environment,
providing various utilities such as plugin installation, environment configuration
($PULUMI_HOME), and creation, deletion, and listing of Stacks.
<para/>
LocalWorkspace relies on Pulumi.yaml and Pulumi.{stack}.yaml as the intermediate format
for Project and Stack settings.Modifying ProjectSettings will
alter the Workspace Pulumi.yaml file, and setting config on a Stack will modify the Pulumi.{stack}.yaml file.
This is identical to the behavior of Pulumi CLI driven workspaces.
<para/>
If not provided a working directory - causing LocalWorkspace to create a temp directory,
than the temp directory will be cleaned up on <see cref="M:Pulumi.Automation.LocalWorkspace.Dispose"/>.
</summary>
</member>
<member name="P:Pulumi.Automation.LocalWorkspace.WorkDir">
<inheritdoc/>
</member>
<member name="P:Pulumi.Automation.LocalWorkspace.PulumiHome">
<inheritdoc/>
</member>
<member name="P:Pulumi.Automation.LocalWorkspace.SecretsProvider">
<inheritdoc/>
</member>
<member name="P:Pulumi.Automation.LocalWorkspace.Program">
<inheritdoc/>
</member>
<member name="P:Pulumi.Automation.LocalWorkspace.EnvironmentVariables">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateAsync(Pulumi.Automation.LocalWorkspaceOptions,System.Threading.CancellationToken)">
<summary>
Creates a workspace using the specified options. Used for maximal control and
customization of the underlying environment before any stacks are created or selected.
</summary>
<param name="options">Options used to configure the workspace.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateStackAsync(Pulumi.Automation.InlineProgramArgs)">
<summary>
Creates a Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the specified
inline (in process) <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.Program"/>. This program
is fully debuggable and runs in process. If no <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings"/>
option is specified, default project settings will be created on behalf of the user. Similarly, unless a
<see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
to a new temporary directory provided by the OS.
</summary>
<param name="args">
A set of arguments to initialize a Stack with an inline <see cref="T:Pulumi.Automation.PulumiFn"/> program
that runs in process, as well as any additional customizations to be applied to the
workspace.
</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateStackAsync(Pulumi.Automation.InlineProgramArgs,System.Threading.CancellationToken)">
<summary>
Creates a Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the specified
inline (in process) <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.Program"/>. This program
is fully debuggable and runs in process. If no <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings"/>
option is specified, default project settings will be created on behalf of the user. Similarly, unless a
<see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
to a new temporary directory provided by the OS.
</summary>
<param name="args">
A set of arguments to initialize a Stack with an inline <see cref="T:Pulumi.Automation.PulumiFn"/> program
that runs in process, as well as any additional customizations to be applied to the
workspace.
</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateStackAsync(Pulumi.Automation.LocalProgramArgs)">
<summary>
Creates a Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the local Pulumi CLI program
from the specified <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
files(Pulumi.yaml, Pulumi.{stack}.yaml).
</summary>
<param name="args">
A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
already exists on disk, as well as any additional customizations to be applied to the
workspace.
</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateStackAsync(Pulumi.Automation.LocalProgramArgs,System.Threading.CancellationToken)">
<summary>
Creates a Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the local Pulumi CLI program
from the specified <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
files(Pulumi.yaml, Pulumi.{stack}.yaml).
</summary>
<param name="args">
A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
already exists on disk, as well as any additional customizations to be applied to the
workspace.
</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SelectStackAsync(Pulumi.Automation.InlineProgramArgs)">
<summary>
Selects an existing Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the specified
inline (in process) <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.Program"/>. This program
is fully debuggable and runs in process. If no <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings"/>
option is specified, default project settings will be created on behalf of the user. Similarly, unless a
<see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
to a new temporary directory provided by the OS.
</summary>
<param name="args">
A set of arguments to initialize a Stack with an inline <see cref="T:Pulumi.Automation.PulumiFn"/> program
that runs in process, as well as any additional customizations to be applied to the
workspace.
</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SelectStackAsync(Pulumi.Automation.InlineProgramArgs,System.Threading.CancellationToken)">
<summary>
Selects an existing Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the specified
inline (in process) <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.Program"/>. This program
is fully debuggable and runs in process. If no <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings"/>
option is specified, default project settings will be created on behalf of the user. Similarly, unless a
<see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
to a new temporary directory provided by the OS.
</summary>
<param name="args">
A set of arguments to initialize a Stack with an inline <see cref="T:Pulumi.Automation.PulumiFn"/> program
that runs in process, as well as any additional customizations to be applied to the
workspace.
</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SelectStackAsync(Pulumi.Automation.LocalProgramArgs)">
<summary>
Selects an existing Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the local Pulumi CLI program
from the specified <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
files(Pulumi.yaml, Pulumi.{stack}.yaml).
</summary>
<param name="args">
A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
already exists on disk, as well as any additional customizations to be applied to the
workspace.
</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SelectStackAsync(Pulumi.Automation.LocalProgramArgs,System.Threading.CancellationToken)">
<summary>
Selects an existing Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the local Pulumi CLI program
from the specified <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
files(Pulumi.yaml, Pulumi.{stack}.yaml).
</summary>
<param name="args">
A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
already exists on disk, as well as any additional customizations to be applied to the
workspace.
</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateOrSelectStackAsync(Pulumi.Automation.InlineProgramArgs)">
<summary>
Creates or selects an existing Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the specified
inline (in process) <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.Program"/>. This program
is fully debuggable and runs in process. If no <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings"/>
option is specified, default project settings will be created on behalf of the user. Similarly, unless a
<see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
to a new temporary directory provided by the OS.
</summary>
<param name="args">
A set of arguments to initialize a Stack with an inline <see cref="T:Pulumi.Automation.PulumiFn"/> program
that runs in process, as well as any additional customizations to be applied to the
workspace.
</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateOrSelectStackAsync(Pulumi.Automation.InlineProgramArgs,System.Threading.CancellationToken)">
<summary>
Creates or selects an existing Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the specified
inline (in process) <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.Program"/>. This program
is fully debuggable and runs in process. If no <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings"/>
option is specified, default project settings will be created on behalf of the user. Similarly, unless a
<see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/> option is specified, the working directory will default
to a new temporary directory provided by the OS.
</summary>
<param name="args">
A set of arguments to initialize a Stack with an inline <see cref="T:Pulumi.Automation.PulumiFn"/> program
that runs in process, as well as any additional customizations to be applied to the
workspace.
</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateOrSelectStackAsync(Pulumi.Automation.LocalProgramArgs)">
<summary>
Creates or selects an existing Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the local Pulumi CLI program
from the specified <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
files(Pulumi.yaml, Pulumi.{stack}.yaml).
</summary>
<param name="args">
A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
already exists on disk, as well as any additional customizations to be applied to the
workspace.
</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateOrSelectStackAsync(Pulumi.Automation.LocalProgramArgs,System.Threading.CancellationToken)">
<summary>
Creates or selects an existing Stack with a <see cref="T:Pulumi.Automation.LocalWorkspace"/> utilizing the local Pulumi CLI program
from the specified <see cref="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir"/>. This is a way to create drivers
on top of pre-existing Pulumi programs. This Workspace will pick up any available Settings
files(Pulumi.yaml, Pulumi.{stack}.yaml).
</summary>
<param name="args">
A set of arguments to initialize a Stack with a pre-configured Pulumi CLI program that
already exists on disk, as well as any additional customizations to be applied to the
workspace.
</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.GetProjectSettingsAsync(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SaveProjectSettingsAsync(Pulumi.Automation.ProjectSettings,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.GetStackSettingsAsync(System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SaveStackSettingsAsync(System.String,Pulumi.Automation.StackSettings,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SerializeArgsForOpAsync(System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.PostCommandCallbackAsync(System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.GetConfigValueAsync(System.String,System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.GetConfigAsync(System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SetConfigValueAsync(System.String,System.String,Pulumi.Automation.ConfigValue,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SetConfigAsync(System.String,System.Collections.Generic.IDictionary{System.String,Pulumi.Automation.ConfigValue},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.RemoveConfigValueAsync(System.String,System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.RemoveConfigAsync(System.String,System.Collections.Generic.IEnumerable{System.String},System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.RefreshConfigAsync(System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.WhoAmIAsync(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.CreateStackAsync(System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.SelectStackAsync(System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.RemoveStackAsync(System.String,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.ListStacksAsync(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.InstallPluginAsync(System.String,System.String,Pulumi.Automation.PluginKind,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.RemovePluginAsync(System.String,System.String,Pulumi.Automation.PluginKind,System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="M:Pulumi.Automation.LocalWorkspace.ListPluginsAsync(System.Threading.CancellationToken)">
<inheritdoc/>
</member>
<member name="T:Pulumi.Automation.LocalWorkspaceOptions">
<summary>
Extensibility options to configure a LocalWorkspace; e.g: settings to seed
and environment variables to pass through to every command.
</summary>
</member>
<member name="P:Pulumi.Automation.LocalWorkspaceOptions.WorkDir">
<summary>
The directory to run Pulumi commands and read settings (Pulumi.yaml and Pulumi.{stack}.yaml).
</summary>
</member>
<member name="P:Pulumi.Automation.LocalWorkspaceOptions.PulumiHome">
<summary>
The directory to override for CLI metadata.
</summary>
</member>
<member name="P:Pulumi.Automation.LocalWorkspaceOptions.SecretsProvider">
<summary>
The secrets provider to user for encryption and decryption of stack secrets.
<para/>
See: https://www.pulumi.com/docs/intro/concepts/config/#available-encryption-providers
</summary>
</member>
<member name="P:Pulumi.Automation.LocalWorkspaceOptions.Program">
<summary>
The inline program <see cref="T:Pulumi.Automation.PulumiFn"/> to be used for Preview/Update operations if any.
<para/>
If none is specified, the stack will refer to <see cref="T:Pulumi.Automation.ProjectSettings"/> for this information.
</summary>
</member>
<member name="P:Pulumi.Automation.LocalWorkspaceOptions.EnvironmentVariables">
<summary>
Environment values scoped to the current workspace. These will be supplied to every
Pulumi command.
</summary>
</member>
<member name="P:Pulumi.Automation.LocalWorkspaceOptions.ProjectSettings">
<summary>
The settings object for the current project.
<para/>
If provided when initializing <see cref="T:Pulumi.Automation.LocalWorkspace"/> a project settings
file will be written to when the workspace is initialized via
<see cref="M:Pulumi.Automation.LocalWorkspace.SaveProjectSettingsAsync(Pulumi.Automation.ProjectSettings,System.Threading.CancellationToken)"/>.
</summary>
</member>
<member name="P:Pulumi.Automation.LocalWorkspaceOptions.StackSettings">
<summary>
A map of Stack names and corresponding settings objects.
<para/>
If provided when initializing <see cref="T:Pulumi.Automation.LocalWorkspace"/> stack settings
file(s) will be written to when the workspace is initialized via
<see cref="M:Pulumi.Automation.LocalWorkspace.SaveStackSettingsAsync(System.String,Pulumi.Automation.StackSettings,System.Threading.CancellationToken)"/>.
</summary>
</member>
<member name="T:Pulumi.Automation.PreviewOptions">
<summary>
Options controlling the behavior of an <see cref="M:Pulumi.Automation.WorkspaceStack.PreviewAsync(Pulumi.Automation.PreviewOptions,System.Threading.CancellationToken)"/> operation.
</summary>
</member>
<member name="T:Pulumi.Automation.ProjectBackend">
<summary>
Configuration for the project's Pulumi state storage backend.
</summary>
</member>
<member name="T:Pulumi.Automation.ProjectRuntime">
<summary>
A description of the Project's program runtime and associated metadata.
</summary>
</member>
<member name="T:Pulumi.Automation.ProjectRuntimeName">
<summary>
Supported Pulumi program language runtimes.
</summary>
</member>
<member name="T:Pulumi.Automation.ProjectRuntimeOptions">
<summary>
Various configuration options that apply to different language runtimes.
</summary>
</member>
<member name="P:Pulumi.Automation.ProjectRuntimeOptions.TypeScript">
<summary>
Applies to NodeJS projects only.
<para/>
A boolean that controls whether to use ts-node to execute sources.
</summary>
</member>
<member name="P:Pulumi.Automation.ProjectRuntimeOptions.Binary">
<summary>
Applies to Go and .NET project only.
<para/>
Go: A string that specifies the name of a pre-build executable to look for on your path.
<para/>
.NET: A string that specifies the path of a pre-build .NET assembly.
</summary>
</member>
<member name="P:Pulumi.Automation.ProjectRuntimeOptions.VirtualEnv">
<summary>
Applies to Python projects only.
<para/>
A string that specifies the path to a virtual environment to use when running the program.
</summary>
</member>
<member name="T:Pulumi.Automation.ProjectSettings">
<summary>
A Pulumi project manifest. It describes metadata applying to all sub-stacks created from the project.
</summary>
</member>
<member name="T:Pulumi.Automation.ProjectTemplate">
<summary>
A template used to seed new stacks created from this project.
</summary>
</member>
<member name="T:Pulumi.Automation.ProjectTemplateConfigValue">
<summary>
A placeholder config value for a project template.
</summary>
</member>
<member name="T:Pulumi.Automation.PulumiFn">
<summary>
A Pulumi program as an inline function (in process).
</summary>
</member>
<member name="M:Pulumi.Automation.PulumiFn.Create(System.Func{System.Threading.CancellationToken,System.Threading.Tasks.Task{System.Collections.Generic.IDictionary{System.String,System.Object}}})">
<summary>
Creates an asynchronous inline (in process) pulumi program.
</summary>
<param name="program">An asynchronous pulumi program that takes in a <see cref="T:System.Threading.CancellationToken"/> and returns an output.</param>
</member>
<member name="M:Pulumi.Automation.PulumiFn.Create(System.Func{System.Threading.Tasks.Task{System.Collections.Generic.IDictionary{System.String,System.Object}}})">
<summary>
Creates an asynchronous inline (in process) pulumi program.
</summary>
<param name="program">An asynchronous pulumi program that returns an output.</param>
</member>
<member name="M:Pulumi.Automation.PulumiFn.Create(System.Func{System.Threading.CancellationToken,System.Threading.Tasks.Task})">
<summary>
Creates an asynchronous inline (in process) pulumi program.
</summary>
<param name="program">An asynchronous pulumi program that takes in a <see cref="T:System.Threading.CancellationToken"/>.</param>
</member>
<member name="M:Pulumi.Automation.PulumiFn.Create(System.Func{System.Threading.Tasks.Task})">
<summary>
Creates an asynchronous inline (in process) pulumi program.
</summary>
<param name="program">An asynchronous pulumi program.</param>
</member>
<member name="M:Pulumi.Automation.PulumiFn.Create(System.Func{System.Collections.Generic.IDictionary{System.String,System.Object}})">
<summary>
Creates an inline (in process) pulumi program.
</summary>
<param name="program">A pulumi program that returns an output.</param>
</member>
<member name="M:Pulumi.Automation.PulumiFn.Create(System.Action)">
<summary>
Creates an inline (in process) pulumi program.
</summary>
<param name="program">A pulumi program.</param>
</member>
<member name="M:Pulumi.Automation.PulumiFn.Create``1">
<summary>
Creates an inline (in process) pulumi program via a traditional <see cref="T:Pulumi.Stack"/> implementation.
</summary>
<typeparam name="TStack">The <see cref="T:Pulumi.Stack"/> type.</typeparam>
</member>
<member name="M:Pulumi.Automation.PulumiFn.Create``1(System.IServiceProvider)">
<summary>
Creates an inline (in process) pulumi program via a traditional <see cref="T:Pulumi.Stack"/> implementation.
<para/>
When invoked, a new stack instance will be resolved based
on the provided <typeparamref name="TStack"/> type parameter
using the <paramref name="serviceProvider"/>.
</summary>
<typeparam name="TStack">The <see cref="T:Pulumi.Stack"/> type.</typeparam>
</member>
<member name="T:Pulumi.Automation.RefreshOptions">
<summary>
Options controlling the behavior of an <see cref="M:Pulumi.Automation.WorkspaceStack.RefreshAsync(Pulumi.Automation.RefreshOptions,System.Threading.CancellationToken)"/> operation.
</summary>
</member>
<member name="P:Pulumi.Automation.StackSettings.SecretsProvider">
<summary>
This stack's secrets provider.
</summary>
</member>
<member name="P:Pulumi.Automation.StackSettings.EncryptedKey">
<summary>
This is the KMS-encrypted ciphertext for the data key used for secrets
encryption. Only used for cloud-based secrets providers.
</summary>
</member>
<member name="P:Pulumi.Automation.StackSettings.EncryptionSalt">
<summary>
This is this stack's base64 encoded encryption salt. Only used for
passphrase-based secrets providers.
</summary>
</member>
<member name="P:Pulumi.Automation.StackSettings.Config">
<summary>
This is an optional configuration bag.
</summary>
</member>
<member name="T:Pulumi.Automation.UpdateOptions">
<summary>
Common options controlling the behavior of update actions taken
against an instance of <see cref="T:Pulumi.Automation.WorkspaceStack"/>.
</summary>
</member>
<member name="T:Pulumi.Automation.UpOptions">
<summary>
Options controlling the behavior of an <see cref="M:Pulumi.Automation.WorkspaceStack.UpAsync(Pulumi.Automation.UpOptions,System.Threading.CancellationToken)"/> operation.
</summary>
</member>
<member name="T:Pulumi.Automation.Workspace">
<summary>
Workspace is the execution context containing a single Pulumi project, a program, and multiple stacks.
<para/>
Workspaces are used to manage the execution environment, providing various utilities such as plugin
installation, environment configuration ($PULUMI_HOME), and creation, deletion, and listing of Stacks.
</summary>
</member>
<member name="P:Pulumi.Automation.Workspace.WorkDir">
<summary>
The working directory to run Pulumi CLI commands.
</summary>
</member>
<member name="P:Pulumi.Automation.Workspace.PulumiHome">
<summary>
The directory override for CLI metadata if set.
<para/>
This customizes the location of $PULUMI_HOME where metadata is stored and plugins are installed.
</summary>
</member>
<member name="P:Pulumi.Automation.Workspace.SecretsProvider">
<summary>
The secrets provider to use for encryption and decryption of stack secrets.
<para/>
See: https://www.pulumi.com/docs/intro/concepts/config/#available-encryption-providers
</summary>
</member>
<member name="P:Pulumi.Automation.Workspace.Program">
<summary>
The inline program <see cref="T:Pulumi.Automation.PulumiFn"/> to be used for Preview/Update operations if any.
<para/>
If none is specified, the stack will refer to <see cref="T:Pulumi.Automation.ProjectSettings"/> for this information.
</summary>
</member>
<member name="P:Pulumi.Automation.Workspace.EnvironmentVariables">
<summary>
Environment values scoped to the current workspace. These will be supplied to every Pulumi command.
</summary>
</member>
<member name="M:Pulumi.Automation.Workspace.GetProjectSettingsAsync(System.Threading.CancellationToken)">
<summary>
Returns project settings for the current project if any.
</summary>
</member>
<member name="M:Pulumi.Automation.Workspace.SaveProjectSettingsAsync(Pulumi.Automation.ProjectSettings,System.Threading.CancellationToken)">
<summary>
Overwrites the settings for the current project.
<para/>
There can only be a single project per workspace. Fails if new project name does not match old.
</summary>
<param name="settings">The settings object to save.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.GetStackSettingsAsync(System.String,System.Threading.CancellationToken)">
<summary>
Returns stack settings for the stack matching the specified stack name if any.
</summary>
<param name="stackName">The name of the stack.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.SaveStackSettingsAsync(System.String,Pulumi.Automation.StackSettings,System.Threading.CancellationToken)">
<summary>
Overwrite the settings for the stack matching the specified stack name.
</summary>
<param name="stackName">The name of the stack to operate on.</param>
<param name="settings">The settings object to save.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.SerializeArgsForOpAsync(System.String,System.Threading.CancellationToken)">
<summary>
Hook to provide additional args to every CLI command before they are executed.
<para/>
Provided with a stack name, returns an array of args to append to an invoked command <c>["--config=...", ]</c>.
<para/>
<see cref="T:Pulumi.Automation.LocalWorkspace"/> does not utilize this extensibility point.
</summary>
<param name="stackName">The name of the stack.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.PostCommandCallbackAsync(System.String,System.Threading.CancellationToken)">
<summary>
Hook executed after every command. Called with the stack name.
<para/>
An extensibility point to perform workspace cleanup (CLI operations may create/modify a Pulumi.stack.yaml).
<para/>
<see cref="T:Pulumi.Automation.LocalWorkspace"/> does not utilize this extensibility point.
</summary>
<param name="stackName">The name of the stack.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.GetConfigValueAsync(System.String,System.String,System.Threading.CancellationToken)">
<summary>
Returns the value associated with the specified stack name and key, scoped
to the Workspace.
</summary>
<param name="stackName">The name of the stack to read config from.</param>
<param name="key">The key to use for the config lookup.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.GetConfigAsync(System.String,System.Threading.CancellationToken)">
<summary>
Returns the config map for the specified stack name, scoped to the current Workspace.
</summary>
<param name="stackName">The name of the stack to read config from.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.SetConfigValueAsync(System.String,System.String,Pulumi.Automation.ConfigValue,System.Threading.CancellationToken)">
<summary>
Sets the specified key-value pair in the provided stack's config.
</summary>
<param name="stackName">The name of the stack to operate on.</param>
<param name="key">The config key to set.</param>
<param name="value">The config value to set.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.SetConfigAsync(System.String,System.Collections.Generic.IDictionary{System.String,Pulumi.Automation.ConfigValue},System.Threading.CancellationToken)">
<summary>
Sets all values in the provided config map for the specified stack name.
</summary>
<param name="stackName">The name of the stack to operate on.</param>
<param name="configMap">The config map to upsert against the existing config.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.RemoveConfigValueAsync(System.String,System.String,System.Threading.CancellationToken)">
<summary>
Removes the specified key-value pair from the provided stack's config.
</summary>
<param name="stackName">The name of the stack to operate on.</param>
<param name="key">The config key to remove.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.RemoveConfigAsync(System.String,System.Collections.Generic.IEnumerable{System.String},System.Threading.CancellationToken)">
<summary>
Removes all values in the provided key collection from the config map for the specified stack name.
</summary>
<param name="stackName">The name of the stack to operate on.</param>
<param name="keys">The collection of keys to remove from the underlying config map.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.RefreshConfigAsync(System.String,System.Threading.CancellationToken)">
<summary>
Gets and sets the config map used with the last update for the stack matching the specified stack name.
</summary>
<param name="stackName">The name of the stack to operate on.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.WhoAmIAsync(System.Threading.CancellationToken)">
<summary>
Returns the currently authenticated user.
</summary>
</member>
<member name="M:Pulumi.Automation.Workspace.GetStackAsync(System.Threading.CancellationToken)">
<summary>
Returns a summary of the currently selected stack, if any.
</summary>
</member>
<member name="M:Pulumi.Automation.Workspace.CreateStackAsync(System.String)">
<summary>
Creates and sets a new stack with the specified stack name, failing if one already exists.
</summary>
<param name="stackName">The stack to create.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.CreateStackAsync(System.String,System.Threading.CancellationToken)">
<summary>
Creates and sets a new stack with the specified stack name, failing if one already exists.
</summary>
<param name="stackName">The stack to create.</param>
<param name="cancellationToken">A cancellation token.</param>
<exception cref="T:Pulumi.Automation.Commands.Exceptions.StackAlreadyExistsException">If a stack already exists by the provided name.</exception>
</member>
<member name="M:Pulumi.Automation.Workspace.SelectStackAsync(System.String)">
<summary>
Selects and sets an existing stack matching the stack name, failing if none exists.
</summary>
<param name="stackName">The stack to select.</param>
<exception cref="T:Pulumi.Automation.Commands.Exceptions.StackNotFoundException">If no stack was found by the provided name.</exception>
</member>
<member name="M:Pulumi.Automation.Workspace.SelectStackAsync(System.String,System.Threading.CancellationToken)">
<summary>
Selects and sets an existing stack matching the stack name, failing if none exists.
</summary>
<param name="stackName">The stack to select.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.RemoveStackAsync(System.String,System.Threading.CancellationToken)">
<summary>
Deletes the stack and all associated configuration and history.
</summary>
<param name="stackName">The stack to remove.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.ListStacksAsync(System.Threading.CancellationToken)">
<summary>
Returns all stacks created under the current project.
<para/>
This queries underlying backend and may return stacks not present in the Workspace (as Pulumi.{stack}.yaml files).
</summary>
</member>
<member name="M:Pulumi.Automation.Workspace.InstallPluginAsync(System.String,System.String,Pulumi.Automation.PluginKind,System.Threading.CancellationToken)">
<summary>
Installs a plugin in the Workspace, for example to use cloud providers like AWS or GCP.
</summary>
<param name="name">The name of the plugin.</param>
<param name="version">The version of the plugin e.g. "v1.0.0".</param>
<param name="kind">The kind of plugin e.g. "resource".</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.RemovePluginAsync(System.String,System.String,Pulumi.Automation.PluginKind,System.Threading.CancellationToken)">
<summary>
Removes a plugin from the Workspace matching the specified name and version.
</summary>
<param name="name">The optional name of the plugin.</param>
<param name="versionRange">The optional semver range to check when removing plugins matching the given name e.g. "1.0.0", ">1.0.0".</param>
<param name="kind">The kind of plugin e.g. "resource".</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.Workspace.ListPluginsAsync(System.Threading.CancellationToken)">
<summary>
Returns a list of all plugins installed in the Workspace.
</summary>
</member>
<member name="T:Pulumi.Automation.WorkspaceStack">
<summary>
<see cref="T:Pulumi.Automation.WorkspaceStack"/> is an isolated, independently configurable instance of a
Pulumi program. <see cref="T:Pulumi.Automation.WorkspaceStack"/> exposes methods for the full pulumi lifecycle
(up/preview/refresh/destroy), as well as managing configuration.
<para/>
Multiple stacks are commonly used to denote different phases of development
(such as development, staging, and production) or feature branches (such as
feature-x-dev, jane-feature-x-dev).
<para/>
Will dispose the <see cref="P:Pulumi.Automation.WorkspaceStack.Workspace"/> on <see cref="M:Pulumi.Automation.WorkspaceStack.Dispose"/>.
</summary>
</member>
<member name="P:Pulumi.Automation.WorkspaceStack.Name">
<summary>
The name identifying the Stack.
</summary>
</member>
<member name="P:Pulumi.Automation.WorkspaceStack.Workspace">
<summary>
The Workspace the Stack was created from.
</summary>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.CreateAsync(System.String,Pulumi.Automation.Workspace,System.Threading.CancellationToken)">
<summary>
Creates a new stack using the given workspace, and stack name.
It fails if a stack with that name already exists.
</summary>
<param name="name">The name identifying the stack.</param>
<param name="workspace">The Workspace the Stack was created from.</param>
<param name="cancellationToken">A cancellation token.</param>
<exception cref="T:Pulumi.Automation.Commands.Exceptions.StackAlreadyExistsException">If a stack with the provided name already exists.</exception>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.SelectAsync(System.String,Pulumi.Automation.Workspace,System.Threading.CancellationToken)">
<summary>
Selects stack using the given workspace, and stack name.
It returns an error if the given Stack does not exist.
</summary>
<param name="name">The name identifying the stack.</param>
<param name="workspace">The Workspace the Stack was created from.</param>
<param name="cancellationToken">A cancellation token.</param>
<exception cref="T:Pulumi.Automation.Commands.Exceptions.StackNotFoundException">If a stack with the provided name does not exists.</exception>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.CreateOrSelectAsync(System.String,Pulumi.Automation.Workspace,System.Threading.CancellationToken)">
<summary>
Tries to create a new Stack using the given workspace, and stack name
if the stack does not already exist, or falls back to selecting an
existing stack. If the stack does not exist, it will be created and
selected.
</summary>
<param name="name">The name of the identifying stack.</param>
<param name="workspace">The Workspace the Stack was created from.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.GetConfigValueAsync(System.String,System.Threading.CancellationToken)">
<summary>
Returns the config value associated with the specified key.
</summary>
<param name="key">The key to use for the config lookup.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.GetConfigAsync(System.Threading.CancellationToken)">
<summary>
Returns the full config map associated with the stack in the Workspace.
</summary>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.SetConfigValueAsync(System.String,Pulumi.Automation.ConfigValue,System.Threading.CancellationToken)">
<summary>
Sets the config key-value pair on the Stack in the associated Workspace.
</summary>
<param name="key">The key to set.</param>
<param name="value">The config value to set.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.SetConfigAsync(System.Collections.Generic.IDictionary{System.String,Pulumi.Automation.ConfigValue},System.Threading.CancellationToken)">
<summary>
Sets all specified config values on the stack in the associated Workspace.
</summary>
<param name="configMap">The map of config key-value pairs to set.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.RemoveConfigValueAsync(System.String,System.Threading.CancellationToken)">
<summary>
Removes the specified config key from the Stack in the associated Workspace.
</summary>
<param name="key">The config key to remove.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.RemoveConfigAsync(System.Collections.Generic.IEnumerable{System.String},System.Threading.CancellationToken)">
<summary>
Removes the specified config keys from the Stack in the associated Workspace.
</summary>
<param name="keys">The config keys to remove.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.RefreshConfigAsync(System.Threading.CancellationToken)">
<summary>
Gets and sets the config map used with the last update.
</summary>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.UpAsync(Pulumi.Automation.UpOptions,System.Threading.CancellationToken)">
<summary>
Creates or updates the resources in a stack by executing the program in the Workspace.
<para/>
https://www.pulumi.com/docs/reference/cli/pulumi_up/
</summary>
<param name="options">Options to customize the behavior of the update.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.PreviewAsync(Pulumi.Automation.PreviewOptions,System.Threading.CancellationToken)">
<summary>
Performs a dry-run update to a stack, returning pending changes.
<para/>
https://www.pulumi.com/docs/reference/cli/pulumi_preview/
</summary>
<param name="options">Options to customize the behavior of the update.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.RefreshAsync(Pulumi.Automation.RefreshOptions,System.Threading.CancellationToken)">
<summary>
Compares the current stacks resource state with the state known to exist in the actual
cloud provider. Any such changes are adopted into the current stack.
</summary>
<param name="options">Options to customize the behavior of the refresh.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.DestroyAsync(Pulumi.Automation.DestroyOptions,System.Threading.CancellationToken)">
<summary>
Destroy deletes all resources in a stack, leaving all history and configuration intact.
</summary>
<param name="options">Options to customize the behavior of the destroy.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.GetOutputAsync(System.Threading.CancellationToken)">
<summary>
Gets the current set of Stack outputs from the last <see cref="M:Pulumi.Automation.WorkspaceStack.UpAsync(Pulumi.Automation.UpOptions,System.Threading.CancellationToken)"/>.
</summary>
</member>
<member name="M:Pulumi.Automation.WorkspaceStack.GetHistoryAsync(Pulumi.Automation.HistoryOptions,System.Threading.CancellationToken)">
<summary>
Returns a list summarizing all previews and current results from Stack lifecycle operations (up/preview/refresh/destroy).
</summary>
<param name="options">Options to customize the behavior of the fetch history action.</param>
<param name="cancellationToken">A cancellation token.</param>
</member>
</members>
</doc>

View file

@ -0,0 +1,41 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
namespace Pulumi.Automation
{
internal sealed class PulumiFnInline : PulumiFn
{
private readonly Func<CancellationToken, Task<IDictionary<string, object?>>> _program;
public PulumiFnInline(Func<CancellationToken, Task<IDictionary<string, object?>>> program)
{
this._program = program;
}
internal override async Task<ExceptionDispatchInfo?> InvokeAsync(IRunner runner, CancellationToken cancellationToken)
{
ExceptionDispatchInfo? info = null;
await runner.RunAsync(async () =>
{
try
{
return await this._program(cancellationToken).ConfigureAwait(false);
}
catch (Exception ex)
{
info = ExceptionDispatchInfo.Capture(ex);
throw;
}
}, null).ConfigureAwait(false);
return info;
}
}
}

View file

@ -0,0 +1,52 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
namespace Pulumi.Automation
{
internal class PulumiFn<TStack> : PulumiFn where TStack : Pulumi.Stack
{
private readonly Func<TStack> _stackFactory;
public PulumiFn(Func<TStack> stackFactory)
{
this._stackFactory = stackFactory;
}
internal override async Task<ExceptionDispatchInfo?> InvokeAsync(IRunner runner, CancellationToken cancellationToken)
{
ExceptionDispatchInfo? info = null;
await runner.RunAsync(() =>
{
try
{
return this._stackFactory();
}
// because we are newing a generic, reflection comes in to
// construct the instance. And if there is an exception in
// the constructor of the user-provided TStack, it will be wrapped
// in TargetInvocationException - which is not the exception
// we want to throw to the consumer.
catch (TargetInvocationException ex)
{
info = ex.InnerException != null
? ExceptionDispatchInfo.Capture(ex.InnerException)
: ExceptionDispatchInfo.Capture(ex);
throw;
}
catch (Exception ex)
{
info = ExceptionDispatchInfo.Capture(ex);
throw;
}
}).ConfigureAwait(false);
return info;
}
}
}

View file

@ -0,0 +1,123 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
namespace Pulumi.Automation
{
/// <summary>
/// A Pulumi program as an inline function (in process).
/// </summary>
public abstract class PulumiFn
{
internal PulumiFn()
{
}
internal abstract Task<ExceptionDispatchInfo?> InvokeAsync(IRunner runner, CancellationToken cancellationToken);
/// <summary>
/// Creates an asynchronous inline (in process) pulumi program.
/// </summary>
/// <param name="program">An asynchronous pulumi program that takes in a <see cref="CancellationToken"/> and returns an output.</param>
public static PulumiFn Create(Func<CancellationToken, Task<IDictionary<string, object?>>> program)
=> new PulumiFnInline(program);
/// <summary>
/// Creates an asynchronous inline (in process) pulumi program.
/// </summary>
/// <param name="program">An asynchronous pulumi program that returns an output.</param>
public static PulumiFn Create(Func<Task<IDictionary<string, object?>>> program)
=> new PulumiFnInline(cancellationToken => program());
/// <summary>
/// Creates an asynchronous inline (in process) pulumi program.
/// </summary>
/// <param name="program">An asynchronous pulumi program that takes in a <see cref="CancellationToken"/>.</param>
public static PulumiFn Create(Func<CancellationToken, Task> program)
{
Func<CancellationToken, Task<IDictionary<string, object?>>> wrapper = async cancellationToken =>
{
await program(cancellationToken).ConfigureAwait(false);
return ImmutableDictionary<string, object?>.Empty;
};
return new PulumiFnInline(wrapper);
}
/// <summary>
/// Creates an asynchronous inline (in process) pulumi program.
/// </summary>
/// <param name="program">An asynchronous pulumi program.</param>
public static PulumiFn Create(Func<Task> program)
{
Func<CancellationToken, Task<IDictionary<string, object?>>> wrapper = async cancellationToken =>
{
await program().ConfigureAwait(false);
return ImmutableDictionary<string, object?>.Empty;
};
return new PulumiFnInline(wrapper);
}
/// <summary>
/// Creates an inline (in process) pulumi program.
/// </summary>
/// <param name="program">A pulumi program that returns an output.</param>
public static PulumiFn Create(Func<IDictionary<string, object?>> program)
{
Func<CancellationToken, Task<IDictionary<string, object?>>> wrapper = cancellationToken =>
{
var output = program();
return Task.FromResult(output);
};
return new PulumiFnInline(wrapper);
}
/// <summary>
/// Creates an inline (in process) pulumi program.
/// </summary>
/// <param name="program">A pulumi program.</param>
public static PulumiFn Create(Action program)
=> Create(() => { program(); return ImmutableDictionary<string, object?>.Empty; });
/// <summary>
/// Creates an inline (in process) pulumi program via a traditional <see cref="Pulumi.Stack"/> implementation.
/// </summary>
/// <typeparam name="TStack">The <see cref="Pulumi.Stack"/> type.</typeparam>
public static PulumiFn Create<TStack>()
where TStack : Pulumi.Stack, new()
=> new PulumiFn<TStack>(() => new TStack());
/// <summary>
/// Creates an inline (in process) pulumi program via a traditional <see cref="Pulumi.Stack"/> implementation.
/// <para/>
/// When invoked, a new stack instance will be resolved based
/// on the provided <typeparamref name="TStack"/> type parameter
/// using the <paramref name="serviceProvider"/>.
/// </summary>
/// <typeparam name="TStack">The <see cref="Pulumi.Stack"/> type.</typeparam>
public static PulumiFn Create<TStack>(IServiceProvider serviceProvider)
where TStack : Pulumi.Stack
{
if (serviceProvider is null)
throw new ArgumentNullException(nameof(serviceProvider));
return new PulumiFn<TStack>(
() =>
{
if (serviceProvider is null)
throw new ArgumentNullException(nameof(serviceProvider), $"The provided service provider was null by the time this {nameof(PulumiFn)} was invoked.");
return serviceProvider.GetService(typeof(TStack)) as TStack
?? throw new ApplicationException(
$"Failed to resolve instance of type {typeof(TStack)} from service provider. Register the type with the service provider before this {nameof(PulumiFn)} is invoked.");
});
}
}
}

View file

@ -0,0 +1,16 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
namespace Pulumi.Automation
{
/// <summary>
/// Options controlling the behavior of an <see cref="WorkspaceStack.RefreshAsync(RefreshOptions, System.Threading.CancellationToken)"/> operation.
/// </summary>
public sealed class RefreshOptions : UpdateOptions
{
public bool? ExpectNoChanges { get; set; }
public Action<string>? OnOutput { get; set; }
}
}

View file

@ -0,0 +1,82 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using Grpc.Core;
using Pulumirpc;
namespace Pulumi.Automation
{
internal class LanguageRuntimeService : LanguageRuntime.LanguageRuntimeBase
{
// MaxRpcMesageSize raises the gRPC Max Message size from `4194304` (4mb) to `419430400` (400mb)
public const int MaxRpcMesageSize = 1024 * 1024 * 400;
private readonly CallerContext _callerContext;
public LanguageRuntimeService(CallerContext callerContext)
{
this._callerContext = callerContext;
}
public override Task<GetRequiredPluginsResponse> GetRequiredPlugins(GetRequiredPluginsRequest request, ServerCallContext context)
{
var response = new GetRequiredPluginsResponse();
return Task.FromResult(response);
}
public override async Task<RunResponse> Run(RunRequest request, ServerCallContext context)
{
if (this._callerContext.CancellationToken.IsCancellationRequested // if caller of UpAsync has cancelled
|| context.CancellationToken.IsCancellationRequested) // if CLI has cancelled
{
return new RunResponse();
}
var args = request.Args;
var engineAddr = args != null && args.Any() ? args[0] : "";
var settings = new InlineDeploymentSettings(
engineAddr,
request.MonitorAddress,
request.Config,
request.Project,
request.Stack,
request.Parallel,
request.DryRun);
using var cts = CancellationTokenSource.CreateLinkedTokenSource(
this._callerContext.CancellationToken,
context.CancellationToken);
this._callerContext.ExceptionDispatchInfo = await Deployment.RunInlineAsync(
settings,
runner => this._callerContext.Program.InvokeAsync(runner, cts.Token))
.ConfigureAwait(false);
Deployment.Instance = null!;
return new RunResponse();
}
public class CallerContext
{
public PulumiFn Program { get; }
public CancellationToken CancellationToken { get; }
public ExceptionDispatchInfo? ExceptionDispatchInfo { get; set; }
public CallerContext(
PulumiFn program,
CancellationToken cancellationToken)
{
this.Program = program;
this.CancellationToken = cancellationToken;
}
}
}
}

View file

@ -0,0 +1,17 @@
// Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
namespace Pulumi.Automation.Serialization
{
// necessary for constructor deserialization
internal class ConfigValueModel : IJsonModel<ConfigValue>
{
public string Value { get; set; } = null!;
public bool Secret { get; set; }
public ConfigValue Convert()
=> new ConfigValue(this.Value, this.Secret);
}
}

View file

@ -0,0 +1,9 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation.Serialization.Json
{
internal interface IJsonModel<out T>
{
T Convert();
}
}

View file

@ -0,0 +1,12 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Text.Json;
namespace Pulumi.Automation.Serialization.Json
{
internal class LowercaseJsonNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name)
=> name.ToLower();
}
}

View file

@ -0,0 +1,28 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Pulumi.Automation.Serialization.Json
{
// necessary because this version of System.Text.Json
// can't deserialize a type that doesn't have a parameterless constructor
internal class MapToModelJsonConverter<T, TModel> : JsonConverter<T>
where TModel : IJsonModel<T>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var model = JsonSerializer.Deserialize<TModel>(ref reader, options);
if (model is null)
throw new JsonException($"Unable to deserialize [{typeToConvert.FullName}]. Expecting object.");
return model.Convert();
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, options);
}
}
}

View file

@ -0,0 +1,75 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Pulumi.Automation.Serialization.Json
{
internal class ProjectRuntimeJsonConverter : JsonConverter<ProjectRuntime>
{
public override ProjectRuntime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
var runtimeName = DeserializeName(ref reader, typeToConvert);
return new ProjectRuntime(runtimeName);
}
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException($"Unable to deserialize [{typeToConvert.FullName}]. Expecting string or object.");
reader.Read();
if (reader.TokenType != JsonTokenType.PropertyName
|| !string.Equals(nameof(ProjectRuntime.Name), reader.GetString(), StringComparison.OrdinalIgnoreCase))
throw new JsonException($"Unable to deserialize [{typeToConvert.FullName}]. Expecting runtime name property.");
reader.Read();
if (reader.TokenType != JsonTokenType.String)
throw new JsonException($"Unable to deserialize [{typeToConvert.FullName}]. Runtime name property should be a string.");
var name = DeserializeName(ref reader, typeToConvert);
reader.Read();
if (reader.TokenType == JsonTokenType.EndObject)
return new ProjectRuntime(name);
if (reader.TokenType != JsonTokenType.PropertyName
|| !string.Equals(nameof(ProjectRuntime.Options), reader.GetString(), StringComparison.OrdinalIgnoreCase))
throw new JsonException($"Unable to deserialize [{typeToConvert.FullName}]. Expecting runtime options property.");
reader.Read();
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException($"Unable to deserialize [{typeToConvert.FullName}]. Runtime options property should be an object.");
var runtimeOptions = JsonSerializer.Deserialize<ProjectRuntimeOptions>(ref reader, options);
reader.Read(); // read final EndObject token
return new ProjectRuntime(name) { Options = runtimeOptions };
static ProjectRuntimeName DeserializeName(ref Utf8JsonReader reader, Type typeToConvert)
{
var runtimeStr = reader.GetString();
if (string.IsNullOrWhiteSpace(runtimeStr))
throw new JsonException($"A valid runtime name was not provided when deserializing [{typeToConvert.FullName}].");
if (Enum.TryParse<ProjectRuntimeName>(runtimeStr, true, out var runtimeName))
return runtimeName;
throw new JsonException($"Unexpected runtime name of \"{runtimeStr}\" provided when deserializing [{typeToConvert.FullName}].");
}
}
public override void Write(Utf8JsonWriter writer, ProjectRuntime value, JsonSerializerOptions options)
{
if (value.Options is null)
{
writer.WriteStringValue(value.Name.ToString().ToLower());
}
else
{
JsonSerializer.Serialize(writer, value);
}
}
}
}

View file

@ -0,0 +1,71 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Pulumi.Automation.Serialization.Json
{
internal class ResourceChangesJsonConverter : JsonConverter<Dictionary<OperationType, int>>
{
public override Dictionary<OperationType, int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException($"Cannot deserialize [{typeToConvert.FullName}]. Expecing object.");
var dictionary = new Dictionary<OperationType, int>();
reader.Read();
while (reader.TokenType != JsonTokenType.EndObject)
{
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Expecting property name.");
var propertyName = reader.GetString();
if (string.IsNullOrWhiteSpace(propertyName))
throw new JsonException("Unable to retrieve property name.");
var operationType = ConvertToOperationType(propertyName);
reader.Read();
if (reader.TokenType != JsonTokenType.Number
|| !reader.TryGetInt32(out var count))
throw new JsonException("Expecting number.");
dictionary[operationType] = count;
reader.Read();
}
return dictionary;
}
public override void Write(Utf8JsonWriter writer, Dictionary<OperationType, int> value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
private static OperationType ConvertToOperationType(string opType)
{
switch (opType)
{
case "create":
return OperationType.Create;
case "create-replacement":
return OperationType.CreateReplacement;
case "delete":
return OperationType.Delete;
case "delete-replaced":
return OperationType.DeleteReplaced;
case "replace":
return OperationType.Replace;
case "same":
return OperationType.Same;
case "update":
return OperationType.Update;
default:
throw new JsonException($"Invalid operation type: {opType}");
}
}
}
}

View file

@ -0,0 +1,57 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Pulumi.Automation.Serialization.Json
{
internal class StackSettingsConfigValueJsonConverter : JsonConverter<StackSettingsConfigValue>
{
public override StackSettingsConfigValue Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var element = JsonSerializer.Deserialize<JsonElement>(ref reader, options);
// check if plain string
if (element.ValueKind == JsonValueKind.String)
{
var value = element.GetString();
return new StackSettingsConfigValue(value, false);
}
// confirm object
if (element.ValueKind != JsonValueKind.Object)
throw new JsonException($"Unable to deserialize [{typeToConvert.FullName}]. Expecting object if not plain string.");
// check if secure string
var securePropertyName = options.PropertyNamingPolicy?.ConvertName("Secure") ?? "Secure";
if (element.TryGetProperty(securePropertyName, out var secureProperty))
{
if (secureProperty.ValueKind != JsonValueKind.String)
throw new JsonException($"Unable to deserialize [{typeToConvert.FullName}] as a secure string. Expecting a string secret.");
var secret = secureProperty.GetString();
return new StackSettingsConfigValue(secret, true);
}
throw new NotSupportedException("Automation API does not currently support deserializing complex objects from stack settings.");
}
public override void Write(Utf8JsonWriter writer, StackSettingsConfigValue value, JsonSerializerOptions options)
{
// secure string
if (value.IsSecure)
{
var securePropertyName = options.PropertyNamingPolicy?.ConvertName("Secure") ?? "Secure";
writer.WriteStartObject();
writer.WriteString(securePropertyName, value.Value);
writer.WriteEndObject();
}
// plain string
else
{
writer.WriteStringValue(value.Value);
}
}
}
}

View file

@ -0,0 +1,80 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Pulumi.Automation.Serialization.Json
{
internal class SystemObjectJsonConverter : JsonConverter<object>
{
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.True)
{
return true;
}
if (reader.TokenType == JsonTokenType.False)
{
return false;
}
if (reader.TokenType == JsonTokenType.Number)
{
if (reader.TryGetInt64(out long l))
{
return l;
}
return reader.GetDouble();
}
if (reader.TokenType == JsonTokenType.String)
{
if (reader.TryGetDateTime(out DateTime datetime))
{
return datetime;
}
return reader.GetString();
}
if (reader.TokenType == JsonTokenType.StartArray)
{
return JsonSerializer.Deserialize<object[]>(ref reader, options);
}
if (reader.TokenType == JsonTokenType.StartObject)
{
var dictionary = new Dictionary<string, object>();
reader.Read();
while (reader.TokenType != JsonTokenType.EndObject)
{
if (reader.TokenType != JsonTokenType.PropertyName)
throw new JsonException("Expecting property name.");
var propertyName = reader.GetString();
if (string.IsNullOrWhiteSpace(propertyName))
throw new JsonException("Unable to retrieve property name.");
reader.Read();
dictionary[propertyName] = JsonSerializer.Deserialize<object>(ref reader, options);
reader.Read();
}
return dictionary;
}
throw new JsonException("Invalid JSON element.");
}
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
throw new NotSupportedException($"Writing as [{typeof(object).FullName}] is not supported.");
}
}
}

View file

@ -0,0 +1,81 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Text.Json;
using System.Text.Json.Serialization;
using Pulumi.Automation.Serialization.Json;
using Pulumi.Automation.Serialization.Yaml;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace Pulumi.Automation.Serialization
{
internal class LocalSerializer
{
private readonly JsonSerializerOptions _jsonOptions;
private readonly IDeserializer _yamlDeserializer;
private readonly ISerializer _yamlSerializer;
public LocalSerializer()
{
// configure json
this._jsonOptions = BuildJsonSerializerOptions();
// configure yaml
this._yamlDeserializer = BuildYamlDeserializer();
this._yamlSerializer = BuildYamlSerializer();
}
public T DeserializeJson<T>(string content)
=> JsonSerializer.Deserialize<T>(content, this._jsonOptions);
public T DeserializeYaml<T>(string content)
where T : class
=> this._yamlDeserializer.Deserialize<T>(content);
public string SerializeJson<T>(T @object)
=> JsonSerializer.Serialize(@object, this._jsonOptions);
public string SerializeYaml<T>(T @object)
where T : class
=> this._yamlSerializer.Serialize(@object);
public static JsonSerializerOptions BuildJsonSerializerOptions()
{
var options = new JsonSerializerOptions
{
AllowTrailingCommas = true,
IgnoreNullValues = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
options.Converters.Add(new JsonStringEnumConverter(new LowercaseJsonNamingPolicy()));
options.Converters.Add(new SystemObjectJsonConverter());
options.Converters.Add(new MapToModelJsonConverter<ConfigValue, ConfigValueModel>());
options.Converters.Add(new MapToModelJsonConverter<PluginInfo, PluginInfoModel>());
options.Converters.Add(new MapToModelJsonConverter<ProjectSettings, ProjectSettingsModel>());
options.Converters.Add(new MapToModelJsonConverter<StackSummary, StackSummaryModel>());
options.Converters.Add(new MapToModelJsonConverter<UpdateSummary, UpdateSummaryModel>());
options.Converters.Add(new ProjectRuntimeJsonConverter());
options.Converters.Add(new ResourceChangesJsonConverter());
options.Converters.Add(new StackSettingsConfigValueJsonConverter());
return options;
}
public static IDeserializer BuildYamlDeserializer()
=> new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.IgnoreUnmatchedProperties()
.WithTypeConverter(new ProjectRuntimeYamlConverter())
.WithTypeConverter(new StackSettingsConfigValueYamlConverter())
.Build();
public static ISerializer BuildYamlSerializer()
=> new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull)
.WithTypeConverter(new ProjectRuntimeYamlConverter())
.WithTypeConverter(new StackSettingsConfigValueYamlConverter())
.Build();
}
}

View file

@ -0,0 +1,44 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using Pulumi.Automation.Serialization.Json;
namespace Pulumi.Automation.Serialization
{
// necessary for constructor deserialization
internal class PluginInfoModel : IJsonModel<PluginInfo>
{
public string Name { get; set; } = null!;
public string? Path { get; set; }
public string Kind { get; set; } = null!;
public string? Version { get; set; }
public long Size { get; set; }
public DateTimeOffset InstallTime { get; set; }
public DateTimeOffset LastUsedTime { get; set; }
public string? ServerUrl { get; set; }
private PluginKind GetKind()
=> string.Equals(this.Kind, "analyzer", StringComparison.OrdinalIgnoreCase) ? PluginKind.Analyzer
: string.Equals(this.Kind, "language", StringComparison.OrdinalIgnoreCase) ? PluginKind.Language
: string.Equals(this.Kind, "resource", StringComparison.OrdinalIgnoreCase) ? PluginKind.Resource
: throw new InvalidOperationException($"Invalid plugin kind: {this.Kind}");
public PluginInfo Convert()
=> new PluginInfo(
this.Name,
this.Path,
this.GetKind(),
this.Version,
this.Size,
this.InstallTime,
this.LastUsedTime,
this.ServerUrl);
}
}

View file

@ -0,0 +1,43 @@
// Copyright 2016-2021, Pulumi Corporation
using Pulumi.Automation.Serialization.Json;
namespace Pulumi.Automation.Serialization
{
// necessary for constructor deserialization
internal class ProjectSettingsModel : IJsonModel<ProjectSettings>
{
public string? Name { get; set; }
public ProjectRuntime? Runtime { get; set; }
public string? Main { get; set; }
public string? Description { get; set; }
public string? Author { get; set; }
public string? Website { get; set; }
public string? License { get; set; }
public string? Config { get; set; }
public ProjectTemplate? Template { get; set; }
public ProjectBackend? Backend { get; set; }
public ProjectSettings Convert()
=> new ProjectSettings(this.Name!, this.Runtime ?? new ProjectRuntime(ProjectRuntimeName.NodeJS))
{
Main = this.Main,
Description = this.Description,
Author = this.Author,
Website = this.Website,
License = this.License,
Config = this.Config,
Template = this.Template,
Backend = this.Backend,
};
}
}

View file

@ -0,0 +1,32 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using Pulumi.Automation.Serialization.Json;
namespace Pulumi.Automation.Serialization
{
// necessary for constructor deserialization
internal class StackSummaryModel : IJsonModel<StackSummary>
{
public string Name { get; set; } = null!;
public bool Current { get; set; }
public DateTimeOffset? LastUpdate { get; set; }
public bool UpdateInProgress { get; set; }
public int? ResourceCount { get; set; }
public string? Url { get; set; }
public StackSummary Convert()
=> new StackSummary(
this.Name,
this.Current,
this.LastUpdate,
this.UpdateInProgress,
this.ResourceCount,
this.Url);
}
}

View file

@ -0,0 +1,56 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using Pulumi.Automation.Serialization.Json;
namespace Pulumi.Automation.Serialization
{
// necessary for constructor deserialization
internal class UpdateSummaryModel : IJsonModel<UpdateSummary>
{
// pre-update information
public UpdateKind Kind { get; set; }
public DateTimeOffset StartTime { get; set; }
public string? Message { get; set; }
public Dictionary<string, string>? Environment { get; set; }
public Dictionary<string, ConfigValue>? Config { get; set; }
// post-update information
public string? Result { get; set; }
public DateTimeOffset EndTime { get; set; }
public int? Version { get; set; }
public string? Deployment { get; set; }
public Dictionary<OperationType, int>? ResourceChanges { get; set; }
private UpdateState GetResult()
=> string.Equals(this.Result, "not-started", StringComparison.OrdinalIgnoreCase) ? UpdateState.NotStarted
: string.Equals(this.Result, "in-progress", StringComparison.OrdinalIgnoreCase) ? UpdateState.InProgress
: string.Equals(this.Result, "succeeded", StringComparison.OrdinalIgnoreCase) ? UpdateState.Succeeded
: string.Equals(this.Result, "failed", StringComparison.OrdinalIgnoreCase) ? UpdateState.Failed
: throw new InvalidOperationException($"Invalid update result: {this.Result}");
public UpdateSummary Convert()
{
return new UpdateSummary(
this.Kind,
this.StartTime,
this.Message ?? string.Empty,
this.Environment ?? new Dictionary<string, string>(),
this.Config ?? new Dictionary<string, ConfigValue>(),
this.GetResult(),
this.EndTime,
this.Version,
this.Deployment,
this.ResourceChanges);
}
}
}

View file

@ -0,0 +1,91 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Linq;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Pulumi.Automation.Serialization.Yaml
{
internal class ProjectRuntimeOptionsYamlConverter : IYamlTypeConverter
{
private static readonly Type Type = typeof(ProjectRuntimeOptions);
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 =
new Dictionary<string, Func<Scalar, string, Type, object?>>(StringComparer.OrdinalIgnoreCase)
{
[nameof(ProjectRuntimeOptions.Binary)] = (x, p, t) => x.Value,
[nameof(ProjectRuntimeOptions.TypeScript)] = (x, p, t) => x.ReadBoolean(p, t),
[nameof(ProjectRuntimeOptions.VirtualEnv)] = (x, p, t) => x.Value,
};
public bool Accepts(Type type)
=> type == Type;
public object? ReadYaml(IParser parser, Type type)
{
if (!parser.TryConsume<MappingStart>(out _))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting object.");
var values = PropertyNames.ToDictionary(x => x, x => (object?)null, StringComparer.OrdinalIgnoreCase);
do
{
if (!parser.TryConsume<Scalar>(out var propertyNameScalar))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting a property name.");
if (!Readers.TryGetValue(propertyNameScalar.Value, out var readerFunc))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Invalid property [{propertyNameScalar.Value}].");
if (!parser.TryConsume<Scalar>(out var propertyValueScalar))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting a scalar value for [{propertyNameScalar.Value}].");
values[propertyNameScalar.Value] = readerFunc(propertyValueScalar, propertyNameScalar.Value, type);
}
while (!parser.Accept<MappingEnd>(out _));
parser.MoveNext(); // read final MappingEnd event
return new ProjectRuntimeOptions
{
Binary = (string?)values[nameof(ProjectRuntimeOptions.Binary)],
TypeScript = (bool?)values[nameof(ProjectRuntimeOptions.TypeScript)],
VirtualEnv = (string?)values[nameof(ProjectRuntimeOptions.VirtualEnv)],
};
}
public void WriteYaml(IEmitter emitter, object? value, Type type)
{
if (!(value is ProjectRuntimeOptions options))
return;
if (string.IsNullOrWhiteSpace(options.Binary)
&& options.TypeScript is null
&& string.IsNullOrWhiteSpace(options.VirtualEnv))
return;
emitter.Emit(new MappingStart(null, null, false, MappingStyle.Block));
if (!string.IsNullOrWhiteSpace(options.Binary))
{
emitter.Emit(new Scalar("binary"));
emitter.Emit(new Scalar(options.Binary));
}
if (options.TypeScript != null)
{
emitter.Emit(new Scalar("typescript"));
emitter.Emit(new Scalar(options.TypeScript.ToString()!.ToLower()));
}
if (!string.IsNullOrWhiteSpace(options.VirtualEnv))
{
emitter.Emit(new Scalar("virtualenv"));
emitter.Emit(new Scalar(options.VirtualEnv));
}
emitter.Emit(new MappingEnd());
}
}
}

View file

@ -0,0 +1,96 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Pulumi.Automation.Serialization.Yaml
{
internal class ProjectRuntimeYamlConverter : IYamlTypeConverter
{
private static readonly Type Type = typeof(ProjectRuntime);
private static readonly Type OptionsType = typeof(ProjectRuntimeOptions);
private readonly IYamlTypeConverter _optionsConverter = new ProjectRuntimeOptionsYamlConverter();
public bool Accepts(Type type)
=> type == Type;
public object? ReadYaml(IParser parser, Type type)
{
if (parser.TryConsume<Scalar>(out var nameValueScalar))
{
var runtimeName = DeserializeName(nameValueScalar, type);
return new ProjectRuntime(runtimeName);
}
if (!parser.TryConsume<MappingStart>(out _))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting string or object.");
if (!parser.TryConsume<Scalar>(out var namePropertyScalar)
|| !string.Equals(nameof(ProjectRuntime.Name), namePropertyScalar.Value, StringComparison.OrdinalIgnoreCase))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting runtime name property.");
if (!parser.TryConsume<Scalar>(out var nameValueScalar2))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Runtime name property should be a string.");
var name = DeserializeName(nameValueScalar2, type);
// early mapping end is ok
if (parser.Accept<MappingEnd>(out _))
{
parser.MoveNext(); // read final MappingEnd since Accept doesn't call MoveNext
return new ProjectRuntime(name);
}
if (!parser.TryConsume<Scalar>(out var optionsPropertyScalar)
|| !string.Equals(nameof(ProjectRuntime.Options), optionsPropertyScalar.Value, StringComparison.OrdinalIgnoreCase))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting runtime options property.");
if (!parser.Accept<MappingStart>(out _))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Runtime options property should be an object.");
var runtimeOptionsObj = this._optionsConverter.ReadYaml(parser, OptionsType);
if (!(runtimeOptionsObj is ProjectRuntimeOptions runtimeOptions))
throw new YamlException("There was an issue deserializing the runtime options object.");
parser.MoveNext(); // read final MappingEnd event
return new ProjectRuntime(name) { Options = runtimeOptions };
static ProjectRuntimeName DeserializeName(Scalar nameValueScalar, Type type)
{
if (string.IsNullOrWhiteSpace(nameValueScalar.Value))
throw new YamlException($"A valid runtime name was not provided when deserializing [{type.FullName}].");
if (Enum.TryParse<ProjectRuntimeName>(nameValueScalar.Value, true, out var runtimeName))
return runtimeName;
throw new YamlException($"Unexpected runtime name of \"{nameValueScalar.Value}\" provided when deserializing [{type.FullName}].");
}
}
public void WriteYaml(IEmitter emitter, object? value, Type type)
{
if (!(value is ProjectRuntime runtime))
return;
if (runtime.Options is null)
{
emitter.Emit(new Scalar(runtime.Name.ToString().ToLower()));
}
else
{
emitter.Emit(new MappingStart(null, null, false, MappingStyle.Block));
emitter.Emit(new Scalar("name"));
emitter.Emit(new Scalar(runtime.Name.ToString().ToLower()));
emitter.Emit(new Scalar("options"));
this._optionsConverter.WriteYaml(emitter, runtime.Options, OptionsType);
emitter.Emit(new MappingEnd());
}
}
}
}

View file

@ -0,0 +1,72 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;
namespace Pulumi.Automation.Serialization.Yaml
{
internal class StackSettingsConfigValueYamlConverter : IYamlTypeConverter
{
private static readonly Type Type = typeof(StackSettingsConfigValue);
public bool Accepts(Type type)
=> type == Type;
public object? ReadYaml(IParser parser, Type type)
{
// check if plain string
if (parser.Accept<Scalar>(out var stringValue))
{
parser.MoveNext();
return new StackSettingsConfigValue(stringValue.Value, false);
}
// confirm it is an object
if (!parser.TryConsume<MappingStart>(out _))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting object if not plain string.");
// get first property name
if (!parser.TryConsume<Scalar>(out var firstPropertyName))
throw new YamlException($"Unable to deserialize [{type.FullName}]. Expecting first property name inside object.");
// check if secure string
if (string.Equals("Secure", firstPropertyName.Value, StringComparison.OrdinalIgnoreCase))
{
// secure string
if (!parser.TryConsume<Scalar>(out var securePropertyValue))
throw new YamlException($"Unable to deserialize [{type.FullName}] as a secure string. Expecting a string secret.");
// needs to be 1 mapping end and then return
parser.Require<MappingEnd>();
parser.MoveNext();
return new StackSettingsConfigValue(securePropertyValue.Value, true);
}
else
{
throw new NotSupportedException("Automation API does not currently support deserializing complex objects from stack settings.");
}
}
public void WriteYaml(IEmitter emitter, object? value, Type type)
{
if (!(value is StackSettingsConfigValue configValue))
return;
// secure string
if (configValue.IsSecure)
{
emitter.Emit(new MappingStart(null, null, false, MappingStyle.Block));
emitter.Emit(new Scalar("secure"));
emitter.Emit(new Scalar(configValue.Value));
emitter.Emit(new MappingEnd());
}
// plain string
else
{
emitter.Emit(new Scalar(configValue.Value));
}
}
}
}

View file

@ -0,0 +1,19 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
namespace Pulumi.Automation.Serialization.Yaml
{
internal static class YamlScalarExtensions
{
public static bool ReadBoolean(this Scalar scalar, string propertyName, Type type)
{
if (bool.TryParse(scalar.Value, out var boolean))
return boolean;
throw new YamlException($"Unable to deserialize [{type.FullName}]. Exepecting a boolean for [{propertyName}].");
}
}
}

View file

@ -0,0 +1,31 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic;
namespace Pulumi.Automation
{
public class StackSettings
{
/// <summary>
/// This stack's secrets provider.
/// </summary>
public string? SecretsProvider { get; set; }
/// <summary>
/// This is the KMS-encrypted ciphertext for the data key used for secrets
/// encryption. Only used for cloud-based secrets providers.
/// </summary>
public string? EncryptedKey { get; set; }
/// <summary>
/// This is this stack's base64 encoded encryption salt. Only used for
/// passphrase-based secrets providers.
/// </summary>
public string? EncryptionSalt { get; set; }
/// <summary>
/// This is an optional configuration bag.
/// </summary>
public IDictionary<string, StackSettingsConfigValue>? Config { get; set; }
}
}

View file

@ -0,0 +1,19 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public class StackSettingsConfigValue
{
public string Value { get; }
public bool IsSecure { get; }
public StackSettingsConfigValue(
string value,
bool isSecure)
{
this.Value = value;
this.IsSecure = isSecure;
}
}
}

View file

@ -0,0 +1,37 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
namespace Pulumi.Automation
{
public class StackSummary
{
public string Name { get; }
public bool IsCurrent { get; }
public DateTimeOffset? LastUpdate { get; }
public bool IsUpdateInProgress { get; }
public int? ResourceCount { get; }
public string? Url { get; }
internal StackSummary(
string name,
bool isCurrent,
DateTimeOffset? lastUpdate,
bool isUpdateInProgress,
int? resourceCount,
string? url)
{
this.Name = name;
this.IsCurrent = isCurrent;
this.LastUpdate = lastUpdate;
this.IsUpdateInProgress = isUpdateInProgress;
this.ResourceCount = resourceCount;
this.Url = url;
}
}
}

View file

@ -0,0 +1,23 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
namespace Pulumi.Automation
{
/// <summary>
/// Options controlling the behavior of an <see cref="WorkspaceStack.UpAsync(UpOptions, System.Threading.CancellationToken)"/> operation.
/// </summary>
public sealed class UpOptions : UpdateOptions
{
public bool? ExpectNoChanges { get; set; }
public List<string>? Replace { get; set; }
public bool? TargetDependents { get; set; }
public Action<string>? OnOutput { get; set; }
public PulumiFn? Program { get; set; }
}
}

View file

@ -0,0 +1,21 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Collections.Immutable;
namespace Pulumi.Automation
{
public sealed class UpResult : UpdateResult
{
public IImmutableDictionary<string, OutputValue> Outputs { get; }
internal UpResult(
string standardOutput,
string standardError,
UpdateSummary summary,
IImmutableDictionary<string, OutputValue> outputs)
: base(standardOutput, standardError, summary)
{
this.Outputs = outputs;
}
}
}

View file

@ -0,0 +1,14 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public enum UpdateKind
{
Update,
Preview,
Refresh,
Rename,
Destroy,
Import,
}
}

View file

@ -0,0 +1,19 @@
// Copyright 2016-2021, Pulumi Corporation
using System.Collections.Generic;
namespace Pulumi.Automation
{
/// <summary>
/// Common options controlling the behavior of update actions taken
/// against an instance of <see cref="WorkspaceStack"/>.
/// </summary>
public class UpdateOptions
{
public int? Parallel { get; set; }
public string? Message { get; set; }
public List<string>? Target { get; set; }
}
}

View file

@ -0,0 +1,23 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public class UpdateResult
{
public string StandardOutput { get; }
public string StandardError { get; }
public UpdateSummary Summary { get; }
internal UpdateResult(
string standardOutput,
string standardError,
UpdateSummary summary)
{
this.StandardOutput = standardOutput;
this.StandardError = standardError;
this.Summary = summary;
}
}
}

View file

@ -0,0 +1,12 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public enum UpdateState
{
NotStarted,
InProgress,
Succeeded,
Failed,
}
}

View file

@ -0,0 +1,57 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Pulumi.Automation
{
public sealed class UpdateSummary
{
// pre-update information
public UpdateKind Kind { get; }
public DateTimeOffset StartTime { get; }
public string Message { get; }
public IImmutableDictionary<string, string> Environment { get; }
public IImmutableDictionary<string, ConfigValue> Config { get; }
// post-update information
public UpdateState Result { get; }
public DateTimeOffset EndTime { get; }
public int? Version { get; }
public string? Deployment { get; }
public IImmutableDictionary<OperationType, int>? ResourceChanges { get; }
internal UpdateSummary(
UpdateKind kind,
DateTimeOffset startTime,
string message,
IDictionary<string, string> environment,
IDictionary<string, ConfigValue> config,
UpdateState result,
DateTimeOffset endTime,
int? version,
string? deployment,
IDictionary<OperationType, int>? resourceChanges)
{
this.Kind = kind;
this.StartTime = startTime;
this.Message = message;
this.Environment = environment.ToImmutableDictionary();
this.Config = config.ToImmutableDictionary();
this.Result = result;
this.EndTime = endTime;
this.Version = version;
this.Deployment = deployment;
this.ResourceChanges = resourceChanges?.ToImmutableDictionary();
}
}
}

View file

@ -0,0 +1,14 @@
// Copyright 2016-2021, Pulumi Corporation
namespace Pulumi.Automation
{
public class WhoAmIResult
{
public string User { get; }
public WhoAmIResult(string user)
{
this.User = user;
}
}
}

View file

@ -0,0 +1,289 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Pulumi.Automation.Commands;
using Pulumi.Automation.Commands.Exceptions;
namespace Pulumi.Automation
{
/// <summary>
/// Workspace is the execution context containing a single Pulumi project, a program, and multiple stacks.
/// <para/>
/// Workspaces are used to manage the execution environment, providing various utilities such as plugin
/// installation, environment configuration ($PULUMI_HOME), and creation, deletion, and listing of Stacks.
/// </summary>
public abstract class Workspace : IDisposable
{
private readonly IPulumiCmd _cmd;
internal Workspace(IPulumiCmd cmd)
{
this._cmd = cmd;
}
/// <summary>
/// The working directory to run Pulumi CLI commands.
/// </summary>
public abstract string WorkDir { get; }
/// <summary>
/// The directory override for CLI metadata if set.
/// <para/>
/// This customizes the location of $PULUMI_HOME where metadata is stored and plugins are installed.
/// </summary>
public abstract string? PulumiHome { get; }
/// <summary>
/// The secrets provider to use for encryption and decryption of stack secrets.
/// <para/>
/// See: https://www.pulumi.com/docs/intro/concepts/config/#available-encryption-providers
/// </summary>
public abstract string? SecretsProvider { get; }
/// <summary>
/// The inline program <see cref="PulumiFn"/> to be used for Preview/Update operations if any.
/// <para/>
/// If none is specified, the stack will refer to <see cref="ProjectSettings"/> for this information.
/// </summary>
public abstract PulumiFn? Program { get; set; }
/// <summary>
/// Environment values scoped to the current workspace. These will be supplied to every Pulumi command.
/// </summary>
public abstract IDictionary<string, string>? EnvironmentVariables { get; set; }
/// <summary>
/// Returns project settings for the current project if any.
/// </summary>
public abstract Task<ProjectSettings?> GetProjectSettingsAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Overwrites the settings for the current project.
/// <para/>
/// There can only be a single project per workspace. Fails if new project name does not match old.
/// </summary>
/// <param name="settings">The settings object to save.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task SaveProjectSettingsAsync(ProjectSettings settings, CancellationToken cancellationToken = default);
/// <summary>
/// Returns stack settings for the stack matching the specified stack name if any.
/// </summary>
/// <param name="stackName">The name of the stack.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task<StackSettings?> GetStackSettingsAsync(string stackName, CancellationToken cancellationToken = default);
/// <summary>
/// Overwrite the settings for the stack matching the specified stack name.
/// </summary>
/// <param name="stackName">The name of the stack to operate on.</param>
/// <param name="settings">The settings object to save.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task SaveStackSettingsAsync(string stackName, StackSettings settings, CancellationToken cancellationToken = default);
/// <summary>
/// Hook to provide additional args to every CLI command before they are executed.
/// <para/>
/// Provided with a stack name, returns an array of args to append to an invoked command <c>["--config=...", ]</c>.
/// <para/>
/// <see cref="LocalWorkspace"/> does not utilize this extensibility point.
/// </summary>
/// <param name="stackName">The name of the stack.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task<ImmutableList<string>> SerializeArgsForOpAsync(string stackName, CancellationToken cancellationToken = default);
/// <summary>
/// Hook executed after every command. Called with the stack name.
/// <para/>
/// An extensibility point to perform workspace cleanup (CLI operations may create/modify a Pulumi.stack.yaml).
/// <para/>
/// <see cref="LocalWorkspace"/> does not utilize this extensibility point.
/// </summary>
/// <param name="stackName">The name of the stack.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task PostCommandCallbackAsync(string stackName, CancellationToken cancellationToken = default);
/// <summary>
/// Returns the value associated with the specified stack name and key, scoped
/// to the Workspace.
/// </summary>
/// <param name="stackName">The name of the stack to read config from.</param>
/// <param name="key">The key to use for the config lookup.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task<ConfigValue> GetConfigValueAsync(string stackName, string key, CancellationToken cancellationToken = default);
/// <summary>
/// Returns the config map for the specified stack name, scoped to the current Workspace.
/// </summary>
/// <param name="stackName">The name of the stack to read config from.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task<ImmutableDictionary<string, ConfigValue>> GetConfigAsync(string stackName, CancellationToken cancellationToken = default);
/// <summary>
/// Sets the specified key-value pair in the provided stack's config.
/// </summary>
/// <param name="stackName">The name of the stack to operate on.</param>
/// <param name="key">The config key to set.</param>
/// <param name="value">The config value to set.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task SetConfigValueAsync(string stackName, string key, ConfigValue value, CancellationToken cancellationToken = default);
/// <summary>
/// Sets all values in the provided config map for the specified stack name.
/// </summary>
/// <param name="stackName">The name of the stack to operate on.</param>
/// <param name="configMap">The config map to upsert against the existing config.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task SetConfigAsync(string stackName, IDictionary<string, ConfigValue> configMap, CancellationToken cancellationToken = default);
/// <summary>
/// Removes the specified key-value pair from the provided stack's config.
/// </summary>
/// <param name="stackName">The name of the stack to operate on.</param>
/// <param name="key">The config key to remove.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task RemoveConfigValueAsync(string stackName, string key, CancellationToken cancellationToken = default);
/// <summary>
/// Removes all values in the provided key collection from the config map for the specified stack name.
/// </summary>
/// <param name="stackName">The name of the stack to operate on.</param>
/// <param name="keys">The collection of keys to remove from the underlying config map.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task RemoveConfigAsync(string stackName, IEnumerable<string> keys, CancellationToken cancellationToken = default);
/// <summary>
/// Gets and sets the config map used with the last update for the stack matching the specified stack name.
/// </summary>
/// <param name="stackName">The name of the stack to operate on.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task<ImmutableDictionary<string, ConfigValue>> RefreshConfigAsync(string stackName, CancellationToken cancellationToken = default);
/// <summary>
/// Returns the currently authenticated user.
/// </summary>
public abstract Task<WhoAmIResult> WhoAmIAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Returns a summary of the currently selected stack, if any.
/// </summary>
public virtual async Task<StackSummary?> GetStackAsync(CancellationToken cancellationToken = default)
{
var stacks = await this.ListStacksAsync(cancellationToken).ConfigureAwait(false);
return stacks.FirstOrDefault(x => x.IsCurrent);
}
/// <summary>
/// Creates and sets a new stack with the specified stack name, failing if one already exists.
/// </summary>
/// <param name="stackName">The stack to create.</param>
public Task CreateStackAsync(string stackName)
=> this.CreateStackAsync(stackName, default);
/// <summary>
/// Creates and sets a new stack with the specified stack name, failing if one already exists.
/// </summary>
/// <param name="stackName">The stack to create.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <exception cref="StackAlreadyExistsException">If a stack already exists by the provided name.</exception>
public abstract Task CreateStackAsync(string stackName, CancellationToken cancellationToken);
/// <summary>
/// Selects and sets an existing stack matching the stack name, failing if none exists.
/// </summary>
/// <param name="stackName">The stack to select.</param>
/// <exception cref="StackNotFoundException">If no stack was found by the provided name.</exception>
public Task SelectStackAsync(string stackName)
=> this.SelectStackAsync(stackName, default);
/// <summary>
/// Selects and sets an existing stack matching the stack name, failing if none exists.
/// </summary>
/// <param name="stackName">The stack to select.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task SelectStackAsync(string stackName, CancellationToken cancellationToken);
/// <summary>
/// Deletes the stack and all associated configuration and history.
/// </summary>
/// <param name="stackName">The stack to remove.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task RemoveStackAsync(string stackName, CancellationToken cancellationToken = default);
/// <summary>
/// Returns all stacks created under the current project.
/// <para/>
/// This queries underlying backend and may return stacks not present in the Workspace (as Pulumi.{stack}.yaml files).
/// </summary>
public abstract Task<ImmutableList<StackSummary>> ListStacksAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Installs a plugin in the Workspace, for example to use cloud providers like AWS or GCP.
/// </summary>
/// <param name="name">The name of the plugin.</param>
/// <param name="version">The version of the plugin e.g. "v1.0.0".</param>
/// <param name="kind">The kind of plugin e.g. "resource".</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task InstallPluginAsync(string name, string version, PluginKind kind = PluginKind.Resource, CancellationToken cancellationToken = default);
/// <summary>
/// Removes a plugin from the Workspace matching the specified name and version.
/// </summary>
/// <param name="name">The optional name of the plugin.</param>
/// <param name="versionRange">The optional semver range to check when removing plugins matching the given name e.g. "1.0.0", ">1.0.0".</param>
/// <param name="kind">The kind of plugin e.g. "resource".</param>
/// <param name="cancellationToken">A cancellation token.</param>
public abstract Task RemovePluginAsync(string? name = null, string? versionRange = null, PluginKind kind = PluginKind.Resource, CancellationToken cancellationToken = default);
/// <summary>
/// Returns a list of all plugins installed in the Workspace.
/// </summary>
public abstract Task<ImmutableList<PluginInfo>> ListPluginsAsync(CancellationToken cancellationToken = default);
internal async Task<CommandResult> RunStackCommandAsync(
string stackName,
IEnumerable<string> args,
Action<string>? onOutput,
CancellationToken cancellationToken)
{
var additionalArgs = await this.SerializeArgsForOpAsync(stackName, cancellationToken).ConfigureAwait(false);
var completeArgs = args.Concat(additionalArgs).ToList();
var result = await this.RunCommandAsync(completeArgs, onOutput, cancellationToken).ConfigureAwait(false);
await this.PostCommandCallbackAsync(stackName, cancellationToken).ConfigureAwait(false);
return result;
}
internal Task<CommandResult> RunCommandAsync(
IEnumerable<string> args,
CancellationToken cancellationToken)
=> this.RunCommandAsync(args, null, cancellationToken);
internal Task<CommandResult> RunCommandAsync(
IEnumerable<string> args,
Action<string>? onOutput,
CancellationToken cancellationToken)
{
var env = new Dictionary<string, string>();
if (!string.IsNullOrWhiteSpace(this.PulumiHome))
env["PULUMI_HOME"] = this.PulumiHome;
if (this.EnvironmentVariables != null)
{
foreach (var pair in this.EnvironmentVariables)
env[pair.Key] = pair.Value;
}
return this._cmd.RunAsync(args, this.WorkDir, env, onOutput, cancellationToken);
}
public virtual void Dispose()
{
}
}
}

View file

@ -0,0 +1,681 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Runtime.ExceptionServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Pulumi.Automation.Commands;
using Pulumi.Automation.Commands.Exceptions;
using Pulumi.Automation.Serialization;
namespace Pulumi.Automation
{
/// <summary>
/// <see cref="WorkspaceStack"/> is an isolated, independently configurable instance of a
/// Pulumi program. <see cref="WorkspaceStack"/> exposes methods for the full pulumi lifecycle
/// (up/preview/refresh/destroy), as well as managing configuration.
/// <para/>
/// Multiple stacks are commonly used to denote different phases of development
/// (such as development, staging, and production) or feature branches (such as
/// feature-x-dev, jane-feature-x-dev).
/// <para/>
/// Will dispose the <see cref="Workspace"/> on <see cref="Dispose"/>.
/// </summary>
public sealed class WorkspaceStack : IDisposable
{
private readonly Task _readyTask;
/// <summary>
/// The name identifying the Stack.
/// </summary>
public string Name { get; }
/// <summary>
/// The Workspace the Stack was created from.
/// </summary>
public Workspace Workspace { get; }
/// <summary>
/// Creates a new stack using the given workspace, and stack name.
/// It fails if a stack with that name already exists.
/// </summary>
/// <param name="name">The name identifying the stack.</param>
/// <param name="workspace">The Workspace the Stack was created from.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <exception cref="StackAlreadyExistsException">If a stack with the provided name already exists.</exception>
public static async Task<WorkspaceStack> CreateAsync(
string name,
Workspace workspace,
CancellationToken cancellationToken = default)
{
var stack = new WorkspaceStack(name, workspace, WorkspaceStackInitMode.Create, cancellationToken);
await stack._readyTask.ConfigureAwait(false);
return stack;
}
/// <summary>
/// Selects stack using the given workspace, and stack name.
/// It returns an error if the given Stack does not exist.
/// </summary>
/// <param name="name">The name identifying the stack.</param>
/// <param name="workspace">The Workspace the Stack was created from.</param>
/// <param name="cancellationToken">A cancellation token.</param>
/// <exception cref="StackNotFoundException">If a stack with the provided name does not exists.</exception>
public static async Task<WorkspaceStack> SelectAsync(
string name,
Workspace workspace,
CancellationToken cancellationToken = default)
{
var stack = new WorkspaceStack(name, workspace, WorkspaceStackInitMode.Select, cancellationToken);
await stack._readyTask.ConfigureAwait(false);
return stack;
}
/// <summary>
/// Tries to create a new Stack using the given workspace, and stack name
/// if the stack does not already exist, or falls back to selecting an
/// existing stack. If the stack does not exist, it will be created and
/// selected.
/// </summary>
/// <param name="name">The name of the identifying stack.</param>
/// <param name="workspace">The Workspace the Stack was created from.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public static async Task<WorkspaceStack> CreateOrSelectAsync(
string name,
Workspace workspace,
CancellationToken cancellationToken = default)
{
var stack = new WorkspaceStack(name, workspace, WorkspaceStackInitMode.CreateOrSelect, cancellationToken);
await stack._readyTask.ConfigureAwait(false);
return stack;
}
private WorkspaceStack(
string name,
Workspace workspace,
WorkspaceStackInitMode mode,
CancellationToken cancellationToken)
{
this.Name = name;
this.Workspace = workspace;
switch (mode)
{
case WorkspaceStackInitMode.Create:
this._readyTask = workspace.CreateStackAsync(name, cancellationToken);
break;
case WorkspaceStackInitMode.Select:
this._readyTask = workspace.SelectStackAsync(name, cancellationToken);
break;
case WorkspaceStackInitMode.CreateOrSelect:
this._readyTask = Task.Run(async () =>
{
try
{
await workspace.CreateStackAsync(name, cancellationToken).ConfigureAwait(false);
}
catch (StackAlreadyExistsException)
{
await workspace.SelectStackAsync(name, cancellationToken).ConfigureAwait(false);
}
});
break;
default:
throw new InvalidOperationException($"Unexpected Stack creation mode: {mode}");
}
}
/// <summary>
/// Returns the config value associated with the specified key.
/// </summary>
/// <param name="key">The key to use for the config lookup.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public Task<ConfigValue> GetConfigValueAsync(string key, CancellationToken cancellationToken = default)
=> this.Workspace.GetConfigValueAsync(this.Name, key, cancellationToken);
/// <summary>
/// Returns the full config map associated with the stack in the Workspace.
/// </summary>
/// <param name="cancellationToken">A cancellation token.</param>
public Task<ImmutableDictionary<string, ConfigValue>> GetConfigAsync(CancellationToken cancellationToken = default)
=> this.Workspace.GetConfigAsync(this.Name, cancellationToken);
/// <summary>
/// Sets the config key-value pair on the Stack in the associated Workspace.
/// </summary>
/// <param name="key">The key to set.</param>
/// <param name="value">The config value to set.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public Task SetConfigValueAsync(string key, ConfigValue value, CancellationToken cancellationToken = default)
=> this.Workspace.SetConfigValueAsync(this.Name, key, value, cancellationToken);
/// <summary>
/// Sets all specified config values on the stack in the associated Workspace.
/// </summary>
/// <param name="configMap">The map of config key-value pairs to set.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public Task SetConfigAsync(IDictionary<string, ConfigValue> configMap, CancellationToken cancellationToken = default)
=> this.Workspace.SetConfigAsync(this.Name, configMap, cancellationToken);
/// <summary>
/// Removes the specified config key from the Stack in the associated Workspace.
/// </summary>
/// <param name="key">The config key to remove.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public Task RemoveConfigValueAsync(string key, CancellationToken cancellationToken = default)
=> this.Workspace.RemoveConfigValueAsync(this.Name, key, cancellationToken);
/// <summary>
/// Removes the specified config keys from the Stack in the associated Workspace.
/// </summary>
/// <param name="keys">The config keys to remove.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public Task RemoveConfigAsync(IEnumerable<string> keys, CancellationToken cancellationToken = default)
=> this.Workspace.RemoveConfigAsync(this.Name, keys, cancellationToken);
/// <summary>
/// Gets and sets the config map used with the last update.
/// </summary>
/// <param name="cancellationToken">A cancellation token.</param>
public Task<ImmutableDictionary<string, ConfigValue>> RefreshConfigAsync(CancellationToken cancellationToken = default)
=> this.Workspace.RefreshConfigAsync(this.Name, cancellationToken);
/// <summary>
/// Creates or updates the resources in a stack by executing the program in the Workspace.
/// <para/>
/// https://www.pulumi.com/docs/reference/cli/pulumi_up/
/// </summary>
/// <param name="options">Options to customize the behavior of the update.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public async Task<UpResult> UpAsync(
UpOptions? options = null,
CancellationToken cancellationToken = default)
{
await this.Workspace.SelectStackAsync(this.Name, cancellationToken).ConfigureAwait(false);
var execKind = ExecKind.Local;
var program = this.Workspace.Program;
var args = new List<string>()
{
"up",
"--yes",
"--skip-preview",
};
if (options != null)
{
if (options.Program != null)
program = options.Program;
if (!string.IsNullOrWhiteSpace(options.Message))
{
args.Add("--message");
args.Add(options.Message);
}
if (options.ExpectNoChanges is true)
args.Add("--expect-no-changes");
if (options.Replace?.Any() == true)
{
foreach (var item in options.Replace)
{
args.Add("--replace");
args.Add(item);
}
}
if (options.Target?.Any() == true)
{
foreach (var item in options.Target)
{
args.Add("--target");
args.Add(item);
}
}
if (options.TargetDependents is true)
args.Add("--target-dependents");
if (options.Parallel.HasValue)
{
args.Add("--parallel");
args.Add(options.Parallel.Value.ToString());
}
}
InlineLanguageHost? inlineHost = null;
try
{
if (program != null)
{
execKind = ExecKind.Inline;
inlineHost = new InlineLanguageHost(program, cancellationToken);
await inlineHost.StartAsync().ConfigureAwait(false);
var port = await inlineHost.GetPortAsync().ConfigureAwait(false);
args.Add($"--client=127.0.0.1:{port}");
}
args.Add("--exec-kind");
args.Add(execKind);
var upResult = await this.RunCommandAsync(args, options?.OnOutput, cancellationToken).ConfigureAwait(false);
if (inlineHost != null && inlineHost.TryGetExceptionInfo(out var exceptionInfo))
exceptionInfo.Throw();
var output = await this.GetOutputAsync(cancellationToken).ConfigureAwait(false);
var summary = await this.GetInfoAsync(cancellationToken).ConfigureAwait(false);
return new UpResult(
upResult.StandardOutput,
upResult.StandardError,
summary!,
output);
}
finally
{
if (inlineHost != null)
{
await inlineHost.DisposeAsync().ConfigureAwait(false);
}
}
}
/// <summary>
/// Performs a dry-run update to a stack, returning pending changes.
/// <para/>
/// https://www.pulumi.com/docs/reference/cli/pulumi_preview/
/// </summary>
/// <param name="options">Options to customize the behavior of the update.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public async Task<UpdateResult> PreviewAsync(
PreviewOptions? options = null,
CancellationToken cancellationToken = default)
{
await this.Workspace.SelectStackAsync(this.Name, cancellationToken).ConfigureAwait(false);
var execKind = ExecKind.Local;
var program = this.Workspace.Program;
var args = new List<string>() { "preview" };
if (options != null)
{
if (options.Program != null)
program = options.Program;
if (!string.IsNullOrWhiteSpace(options.Message))
{
args.Add("--message");
args.Add(options.Message);
}
if (options.ExpectNoChanges is true)
args.Add("--expect-no-changes");
if (options.Replace?.Any() == true)
{
foreach (var item in options.Replace)
{
args.Add("--replace");
args.Add(item);
}
}
if (options.Target?.Any() == true)
{
foreach (var item in options.Target)
{
args.Add("--target");
args.Add(item);
}
}
if (options.TargetDependents is true)
args.Add("--target-dependents");
if (options.Parallel.HasValue)
{
args.Add("--parallel");
args.Add(options.Parallel.Value.ToString());
}
}
InlineLanguageHost? inlineHost = null;
try
{
if (program != null)
{
execKind = ExecKind.Inline;
inlineHost = new InlineLanguageHost(program, cancellationToken);
await inlineHost.StartAsync().ConfigureAwait(false);
var port = await inlineHost.GetPortAsync().ConfigureAwait(false);
args.Add($"--client=127.0.0.1:{port}");
}
args.Add("--exec-kind");
args.Add(execKind);
var upResult = await this.RunCommandAsync(args, null, cancellationToken).ConfigureAwait(false);
if (inlineHost != null && inlineHost.TryGetExceptionInfo(out var exceptionInfo))
exceptionInfo.Throw();
var summary = await this.GetInfoAsync(cancellationToken).ConfigureAwait(false);
return new UpdateResult(
upResult.StandardOutput,
upResult.StandardError,
summary!);
}
finally
{
if (inlineHost != null)
{
await inlineHost.DisposeAsync().ConfigureAwait(false);
}
}
}
/// <summary>
/// Compares the current stacks resource state with the state known to exist in the actual
/// cloud provider. Any such changes are adopted into the current stack.
/// </summary>
/// <param name="options">Options to customize the behavior of the refresh.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public async Task<UpdateResult> RefreshAsync(
RefreshOptions? options = null,
CancellationToken cancellationToken = default)
{
await this.Workspace.SelectStackAsync(this.Name, cancellationToken).ConfigureAwait(false);
var args = new List<string>()
{
"refresh",
"--yes",
"--skip-preview",
};
if (options != null)
{
if (!string.IsNullOrWhiteSpace(options.Message))
{
args.Add("--message");
args.Add(options.Message);
}
if (options.ExpectNoChanges is true)
args.Add("--expect-no-changes");
if (options.Target?.Any() == true)
{
foreach (var item in options.Target)
{
args.Add("--target");
args.Add(item);
}
}
if (options.Parallel.HasValue)
{
args.Add("--parallel");
args.Add(options.Parallel.Value.ToString());
}
}
var result = await this.RunCommandAsync(args, options?.OnOutput, cancellationToken).ConfigureAwait(false);
var summary = await this.GetInfoAsync(cancellationToken).ConfigureAwait(false);
return new UpdateResult(
result.StandardOutput,
result.StandardError,
summary!);
}
/// <summary>
/// Destroy deletes all resources in a stack, leaving all history and configuration intact.
/// </summary>
/// <param name="options">Options to customize the behavior of the destroy.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public async Task<UpdateResult> DestroyAsync(
DestroyOptions? options = null,
CancellationToken cancellationToken = default)
{
await this.Workspace.SelectStackAsync(this.Name, cancellationToken).ConfigureAwait(false);
var args = new List<string>()
{
"destroy",
"--yes",
"--skip-preview",
};
if (options != null)
{
if (!string.IsNullOrWhiteSpace(options.Message))
{
args.Add("--message");
args.Add(options.Message);
}
if (options.Target?.Any() == true)
{
foreach (var item in options.Target)
{
args.Add("--target");
args.Add(item);
}
}
if (options.TargetDependents is true)
args.Add("--target-dependents");
if (options.Parallel.HasValue)
{
args.Add("--parallel");
args.Add(options.Parallel.Value.ToString());
}
}
var result = await this.RunCommandAsync(args, options?.OnOutput, cancellationToken).ConfigureAwait(false);
var summary = await this.GetInfoAsync(cancellationToken).ConfigureAwait(false);
return new UpdateResult(
result.StandardOutput,
result.StandardError,
summary!);
}
/// <summary>
/// Gets the current set of Stack outputs from the last <see cref="UpAsync(UpOptions?, CancellationToken)"/>.
/// </summary>
private async Task<ImmutableDictionary<string, OutputValue>> GetOutputAsync(CancellationToken cancellationToken)
{
await this.Workspace.SelectStackAsync(this.Name).ConfigureAwait(false);
// TODO: do this in parallel after this is fixed https://github.com/pulumi/pulumi/issues/6050
var maskedResult = await this.RunCommandAsync(new[] { "stack", "output", "--json" }, null, cancellationToken).ConfigureAwait(false);
var plaintextResult = await this.RunCommandAsync(new[] { "stack", "output", "--json", "--show-secrets" }, null, cancellationToken).ConfigureAwait(false);
var jsonOptions = LocalSerializer.BuildJsonSerializerOptions();
var maskedOutput = JsonSerializer.Deserialize<Dictionary<string, object>>(maskedResult.StandardOutput, jsonOptions);
var plaintextOutput = JsonSerializer.Deserialize<Dictionary<string, object>>(plaintextResult.StandardOutput, jsonOptions);
var output = new Dictionary<string, OutputValue>();
foreach (var (key, value) in plaintextOutput)
{
var secret = maskedOutput[key] is string maskedValue && maskedValue == "[secret]";
output[key] = new OutputValue(value, secret);
}
return output.ToImmutableDictionary();
}
/// <summary>
/// Returns a list summarizing all previews and current results from Stack lifecycle operations (up/preview/refresh/destroy).
/// </summary>
/// <param name="options">Options to customize the behavior of the fetch history action.</param>
/// <param name="cancellationToken">A cancellation token.</param>
public async Task<ImmutableList<UpdateSummary>> GetHistoryAsync(
HistoryOptions? options = null,
CancellationToken cancellationToken = default)
{
var args = new List<string>()
{
"history",
"--json",
"--show-secrets",
};
if (options?.PageSize.HasValue == true)
{
if (options.PageSize!.Value < 1)
throw new ArgumentException($"{nameof(options.PageSize)} must be greater than or equal to 1.", nameof(options.PageSize));
var page = !options.Page.HasValue ? 1
: options.Page.Value < 1 ? 1
: options.Page.Value;
args.Add("--page-size");
args.Add(options.PageSize.Value.ToString());
args.Add("--page");
args.Add(page.ToString());
}
var result = await this.RunCommandAsync(args, null, cancellationToken).ConfigureAwait(false);
var jsonOptions = LocalSerializer.BuildJsonSerializerOptions();
var list = JsonSerializer.Deserialize<List<UpdateSummary>>(result.StandardOutput, jsonOptions);
return list.ToImmutableList();
}
public async Task<UpdateSummary?> GetInfoAsync(CancellationToken cancellationToken = default)
{
var history = await this.GetHistoryAsync(
new HistoryOptions
{
PageSize = 1,
},
cancellationToken).ConfigureAwait(false);
return history.FirstOrDefault();
}
private Task<CommandResult> RunCommandAsync(
IEnumerable<string> args,
Action<string>? onOutput,
CancellationToken cancellationToken)
=> this.Workspace.RunStackCommandAsync(this.Name, args, onOutput, cancellationToken);
public void Dispose()
=> this.Workspace.Dispose();
private static class ExecKind
{
public const string Local = "auto.local";
public const string Inline = "auto.inline";
}
private enum WorkspaceStackInitMode
{
Create,
Select,
CreateOrSelect
}
private class InlineLanguageHost : IAsyncDisposable
{
private readonly TaskCompletionSource<int> _portTcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly CancellationToken _cancelToken;
private readonly IHost _host;
private readonly CancellationTokenRegistration _portRegistration;
public InlineLanguageHost(
PulumiFn program,
CancellationToken cancellationToken)
{
this._cancelToken = cancellationToken;
this._host = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.ConfigureKestrel(kestrelOptions =>
{
kestrelOptions.Listen(IPAddress.Any, 0, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
})
.ConfigureServices(services =>
{
services.AddLogging();
// to be injected into LanguageRuntimeService
var callerContext = new LanguageRuntimeService.CallerContext(program, cancellationToken);
services.AddSingleton(callerContext);
services.AddGrpc(grpcOptions =>
{
grpcOptions.MaxReceiveMessageSize = LanguageRuntimeService.MaxRpcMesageSize;
grpcOptions.MaxSendMessageSize = LanguageRuntimeService.MaxRpcMesageSize;
});
})
.Configure(app =>
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<LanguageRuntimeService>();
});
});
})
.Build();
// before starting the host, set up this callback to tell us what port was selected
this._portRegistration = this._host.Services.GetRequiredService<IHostApplicationLifetime>().ApplicationStarted.Register(() =>
{
try
{
var serverFeatures = this._host.Services.GetRequiredService<IServer>().Features;
var addresses = serverFeatures.Get<IServerAddressesFeature>().Addresses.ToList();
Debug.Assert(addresses.Count == 1, "Server should only be listening on one address");
var uri = new Uri(addresses[0]);
this._portTcs.TrySetResult(uri.Port);
}
catch (Exception ex)
{
this._portTcs.TrySetException(ex);
}
});
}
public Task StartAsync()
=> this._host.StartAsync(this._cancelToken);
public Task<int> GetPortAsync()
=> this._portTcs.Task;
public bool TryGetExceptionInfo([NotNullWhen(true)] out ExceptionDispatchInfo? info)
{
var callerContext = this._host.Services.GetRequiredService<LanguageRuntimeService.CallerContext>();
if (callerContext.ExceptionDispatchInfo is null)
{
info = null;
return false;
}
info = callerContext.ExceptionDispatchInfo;
return true;
}
public async ValueTask DisposeAsync()
{
this._portRegistration.Unregister();
await this._host.StopAsync(this._cancelToken).ConfigureAwait(false);
this._host.Dispose();
}
}
}
}

View file

@ -1,4 +1,4 @@
using Xunit;
// Unfortunately, we depend on static state. So for now disable parallelization.
[assembly: CollectionBehavior(DisableTestParallelization = true)]
[assembly: CollectionBehavior(DisableTestParallelization = true)]

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
@ -19,7 +19,9 @@
<ItemGroup>
<!-- Unfortunately, we depend on static state. So we configure xUnit's runner to disable parallelization. -->
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Pulumi.Automation")]
[assembly: InternalsVisibleTo("Pulumi.Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] // Moq tests

View file

@ -43,7 +43,7 @@ namespace Pulumi
public Task<int> RunAsync<TStack>() where TStack : Stack, new()
=> RunAsync(() => new TStack());
private Task<int> RunAsync<TStack>(Func<TStack> stackFactory) where TStack : Stack
public Task<int> RunAsync<TStack>(Func<TStack> stackFactory) where TStack : Stack
{
try
{

View file

@ -1,6 +1,7 @@
// Copyright 2016-2019, Pulumi Corporation
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Text.Json;
@ -24,6 +25,12 @@ namespace Pulumi
internal void SetConfig(string key, string value)
=> AllConfig = AllConfig.Add(key, value);
/// <summary>
/// Appends all provided configuration.
/// </summary>
internal void SetAllConfig(IDictionary<string, string> config)
=> AllConfig = AllConfig.AddRange(config);
/// <summary>
/// Returns a configuration variable's value or <see langword="null"/> if it is unset.
/// </summary>

View file

@ -0,0 +1,44 @@
// Copyright 2016-2021, Pulumi Corporation
using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
namespace Pulumi
{
public partial class Deployment
{
private Deployment(InlineDeploymentSettings settings)
{
if (settings is null)
throw new ArgumentNullException(nameof(settings));
_projectName = settings.Project;
_stackName = settings.Stack;
_isDryRun = settings.IsDryRun;
SetAllConfig(settings.Config);
if (string.IsNullOrEmpty(settings.MonitorAddr)
|| string.IsNullOrEmpty(settings.EngineAddr)
|| string.IsNullOrEmpty(_projectName)
|| string.IsNullOrEmpty(_stackName))
{
throw new InvalidOperationException("Inline execution was not provided the necessary parameters to run the Pulumi engine.");
}
Serilog.Log.Debug("Creating Deployment Engine.");
Engine = new GrpcEngine(settings.EngineAddr);
Serilog.Log.Debug("Created Deployment Engine.");
Serilog.Log.Debug("Creating Deployment Monitor.");
Monitor = new GrpcMonitor(settings.MonitorAddr);
Serilog.Log.Debug("Created Deployment Monitor.");
_runner = new Runner(this);
_logger = new Logger(this, Engine);
}
internal static Task<ExceptionDispatchInfo?> RunInlineAsync(InlineDeploymentSettings settings, Func<IRunner, Task<ExceptionDispatchInfo?>> func)
=> func(CreateRunner(() => new Deployment(settings)));
}
}

View file

@ -72,7 +72,7 @@ namespace Pulumi
/// <param name="func">Callback that creates stack resources.</param>
/// <param name="options">Stack options.</param>
public static Task<int> RunAsync(Func<Task<IDictionary<string, object?>>> func, StackOptions? options = null)
=> CreateRunner().RunAsync(func, options);
=> CreateRunner(() => new Deployment()).RunAsync(func, options);
/// <summary>
/// <see cref="RunAsync{TStack}()"/> is an entry-point to a Pulumi
@ -101,7 +101,7 @@ namespace Pulumi
/// </para>
/// </summary>
public static Task<int> RunAsync<TStack>() where TStack : Stack, new()
=> CreateRunner().RunAsync<TStack>();
=> CreateRunner(() => new Deployment()).RunAsync<TStack>();
/// <summary>
/// <see cref="RunAsync{TStack}()"/> is an entry-point to a Pulumi
@ -131,7 +131,7 @@ namespace Pulumi
/// </para>
/// </summary>
public static Task<int> RunAsync<TStack>(IServiceProvider serviceProvider) where TStack : Stack
=> CreateRunner().RunAsync<TStack>(serviceProvider);
=> CreateRunner(() => new Deployment()).RunAsync<TStack>(serviceProvider);
/// <summary>
/// Entry point to test a Pulumi application. Deployment will
@ -203,7 +203,7 @@ namespace Pulumi
}
}
private static IRunner CreateRunner()
private static IRunner CreateRunner(Func<Deployment> deploymentFactory)
{
// Serilog.Log.Logger = new LoggerConfiguration().MinimumLevel.Debug().WriteTo.Console().CreateLogger();
@ -214,7 +214,7 @@ namespace Pulumi
throw new NotSupportedException("Deployment.Run can only be called a single time.");
Serilog.Log.Debug("Creating new Deployment.");
var deployment = new Deployment();
var deployment = deploymentFactory();
Instance = new DeploymentInstance(deployment);
return deployment._runner;
}

View file

@ -11,6 +11,7 @@ namespace Pulumi
void RegisterTask(string description, Task task);
Task<int> RunAsync(Func<Task<IDictionary<string, object?>>> func, StackOptions? options);
Task<int> RunAsync<TStack>() where TStack : Stack, new();
Task<int> RunAsync<TStack>(Func<TStack> stackFactory) where TStack : Stack;
Task<int> RunAsync<TStack>(IServiceProvider serviceProvider) where TStack : Stack;
}
}

View file

@ -0,0 +1,39 @@
using System.Collections.Generic;
namespace Pulumi
{
internal class InlineDeploymentSettings
{
public string EngineAddr { get; }
public string MonitorAddr { get; }
public IDictionary<string, string> Config { get; }
public string Project { get; }
public string Stack { get; }
public int Parallel { get; }
public bool IsDryRun { get; }
public InlineDeploymentSettings(
string engineAddr,
string monitorAddr,
IDictionary<string, string> config,
string project,
string stack,
int parallel,
bool isDryRun)
{
EngineAddr = engineAddr;
MonitorAddr = monitorAddr;
Config = config;
Project = project;
Stack = stack;
Parallel = parallel;
IsDryRun = isDryRun;
}
}
}

View file

@ -113,7 +113,10 @@ namespace Pulumi
}
}
static bool PossibleMatch(AssemblyName? assembly) => assembly != null && !assembly.FullName.StartsWith("System", StringComparison.Ordinal);
static bool PossibleMatch(AssemblyName? assembly)
=> assembly != null
&& !assembly.FullName.StartsWith("System", StringComparison.Ordinal)
&& assembly.ContentType != AssemblyContentType.WindowsRuntime;
}
}
}

View file

@ -16,6 +16,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pulumi.Automation", "Pulumi.Automation\Pulumi.Automation.csproj", "{74A15689-FB60-4760-99C8-FC0D89883F3D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pulumi.Automation.Tests", "Pulumi.Automation.Tests\Pulumi.Automation.Tests.csproj", "{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -62,6 +66,30 @@ Global
{F45E8B4A-DAF3-48E8-B9D6-01924AF2188D}.Release|x64.Build.0 = Release|Any CPU
{F45E8B4A-DAF3-48E8-B9D6-01924AF2188D}.Release|x86.ActiveCfg = Release|Any CPU
{F45E8B4A-DAF3-48E8-B9D6-01924AF2188D}.Release|x86.Build.0 = Release|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Debug|x64.ActiveCfg = Debug|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Debug|x64.Build.0 = Debug|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Debug|x86.ActiveCfg = Debug|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Debug|x86.Build.0 = Debug|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Release|Any CPU.Build.0 = Release|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Release|x64.ActiveCfg = Release|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Release|x64.Build.0 = Release|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Release|x86.ActiveCfg = Release|Any CPU
{74A15689-FB60-4760-99C8-FC0D89883F3D}.Release|x86.Build.0 = Release|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Debug|x64.ActiveCfg = Debug|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Debug|x64.Build.0 = Debug|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Debug|x86.ActiveCfg = Debug|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Debug|x86.Build.0 = Debug|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Release|Any CPU.Build.0 = Release|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Release|x64.ActiveCfg = Release|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Release|x64.Build.0 = Release|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Release|x86.ActiveCfg = Release|Any CPU
{A1E69FAC-B6C2-4EA2-8A2D-397536CE35B2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE