diff --git a/.changes/unreleased/Improvements-210.yaml b/.changes/unreleased/Improvements-210.yaml new file mode 100644 index 00000000..9918a7bc --- /dev/null +++ b/.changes/unreleased/Improvements-210.yaml @@ -0,0 +1,6 @@ +component: sdk +kind: Improvements +body: Add environment add and remove commands to automation api +time: 2023-12-13T17:49:09.172346903-08:00 +custom: + PR: "210" diff --git a/sdk/Pulumi.Automation.Tests/LocalWorkspaceTests.cs b/sdk/Pulumi.Automation.Tests/LocalWorkspaceTests.cs index 5006f79d..bb38589e 100644 --- a/sdk/Pulumi.Automation.Tests/LocalWorkspaceTests.cs +++ b/sdk/Pulumi.Automation.Tests/LocalWorkspaceTests.cs @@ -22,6 +22,7 @@ using ILogger = Microsoft.Extensions.Logging.ILogger; using static Pulumi.Automation.Tests.Utility; +using Xunit.Sdk; namespace Pulumi.Automation.Tests { @@ -145,6 +146,35 @@ public async Task AddRemoveListPlugins() Assert.DoesNotContain(plugins, p => p.Name == plugin && p.Version == version); } + [MoolumiFact] + public async Task AddAndRemoveEnvironment() + { + var projectName = "node_env_test"; + var projectSettings = new ProjectSettings(projectName, ProjectRuntimeName.NodeJS); + using var workspace = await LocalWorkspace.CreateAsync(new LocalWorkspaceOptions + { + ProjectSettings = projectSettings, + }); + + var program = PulumiFn.Create(); + var stackName = FullyQualifiedStackName(_pulumiOrg, projectName, $"int_test{GetTestSuffix()}"); + await workspace.CreateStackAsync(stackName); + + await Assert.ThrowsAsync(() => workspace.AddEnvironmentsAsync(stackName, new[] { "non-existent-env" })); + + await workspace.AddEnvironmentsAsync(stackName, new[] { "automation-api-test-env", "automation-api-test-env-2" }); + var config = await workspace.GetAllConfigAsync(stackName); + + Assert.Equal("test_value", config["node_env_test:new_key"].Value); + Assert.Equal("business", config["node_env_test:also"].Value); + + await workspace.RemoveEnvironmentAsync(stackName, "automation-api-test-env"); + config = await workspace.GetAllConfigAsync(stackName); + Assert.Equal("business", config["node_env_test:also"].Value); + Assert.False(config.ContainsKey("node_env_test:new_key")); + await workspace.RemoveStackAsync(stackName); + } + [Fact] public async Task CreateSelectRemoveStack() { diff --git a/sdk/Pulumi.Automation.Tests/MoolumiFactAttribute.cs b/sdk/Pulumi.Automation.Tests/MoolumiFactAttribute.cs new file mode 100644 index 00000000..fd55c0c1 --- /dev/null +++ b/sdk/Pulumi.Automation.Tests/MoolumiFactAttribute.cs @@ -0,0 +1,23 @@ +// Copyright 2016-2023, Pulumi Corporation + +using System; +using Xunit; +using static Pulumi.Automation.Tests.Utility; + +namespace Pulumi.Automation.Tests +{ + /// + /// Skip a test if test org is not moolumi. + /// + public sealed class MoolumiFactAttribute : FactAttribute + { + public MoolumiFactAttribute() + { + var testOrg = GetTestOrg(); + if (testOrg != "moolumi") + { + Skip = "Test org is not moolumi"; + } + } + } +} diff --git a/sdk/Pulumi.Automation/LocalWorkspace.cs b/sdk/Pulumi.Automation/LocalWorkspace.cs index 71e1b75d..c5204217 100644 --- a/sdk/Pulumi.Automation/LocalWorkspace.cs +++ b/sdk/Pulumi.Automation/LocalWorkspace.cs @@ -540,6 +540,23 @@ public override Task> SerializeArgsForOpAsync(string stack public override Task PostCommandCallbackAsync(string stackName, CancellationToken cancellationToken = default) => Task.CompletedTask; + + /// + public override async Task AddEnvironmentsAsync(string stackName, IEnumerable environments, CancellationToken cancellationToken = default) + { + CheckSupportsEnvironmentsCommands(); + var args = new List { "config", "env", "add", "--stack", stackName, "--yes" }; + args.AddRange(environments); + await this.RunCommandAsync(args, cancellationToken).ConfigureAwait(false); + } + + /// + public override async Task RemoveEnvironmentAsync(string stackName, string environment, CancellationToken cancellationToken = default) + { + CheckSupportsEnvironmentsCommands(); + await this.RunCommandAsync(new[] { "config", "env", "rm", environment, "--stack", stackName, "--yes" }, cancellationToken).ConfigureAwait(false); + } + /// public override async Task GetTagAsync(string stackName, string key, CancellationToken cancellationToken = default) { @@ -963,5 +980,16 @@ internal IReadOnlyList GetRemoteArgs() return args; } + + private void CheckSupportsEnvironmentsCommands() + { + var version = this._pulumiVersion ?? new SemVersion(3, 0); + + // 3.95 added this command (https://github.com/pulumi/pulumi/releases/tag/v3.95.0) + if (version < new SemVersion(3, 95)) + { + throw new InvalidOperationException("The Pulumi CLI version does not support env operations on a stack. Please update the Pulumi CLI."); + } + } } } diff --git a/sdk/Pulumi.Automation/Pulumi.Automation.xml b/sdk/Pulumi.Automation/Pulumi.Automation.xml index d1e6bf11..9bdee7c5 100644 --- a/sdk/Pulumi.Automation/Pulumi.Automation.xml +++ b/sdk/Pulumi.Automation/Pulumi.Automation.xml @@ -566,6 +566,12 @@ + + + + + + @@ -1442,6 +1448,24 @@ The name of the stack. A cancellation token. + + + Adds environments to the end of a stack's import list. Imported environments are merged in order + per the ESC merge rules. The list of environments behaves as if it were the import list in an anonymous + environment. + + The name of the stack. + List of environments to add to the end of the stack's import list. + A cancellation token. + + + + Removes environments from a stack's import list. + + The name of the stack. + The name of the environment to remove from the stack's configuration. + A cancellation token. + Returns the value associated with the specified stack name and key, @@ -1862,6 +1886,22 @@ A cancellation token. + + + Adds environments to the end of a stack's import list. Imported environments are merged in order + per the ESC merge rules. The list of environments behaves as if it were the import list in an anonymous + environment. + + List of environments to add to the end of the stack's import list. + A cancellation token. + + + + Removes environments from a stack's import list. + + The name of the environment to remove from the stack's configuration. + A cancellation token. + Creates or updates the resources in a stack by executing the program in the Workspace. diff --git a/sdk/Pulumi.Automation/Workspace.cs b/sdk/Pulumi.Automation/Workspace.cs index df4600ae..561a17a1 100644 --- a/sdk/Pulumi.Automation/Workspace.cs +++ b/sdk/Pulumi.Automation/Workspace.cs @@ -123,6 +123,25 @@ internal Workspace(IPulumiCmd cmd) /// A cancellation token. public abstract Task PostCommandCallbackAsync(string stackName, CancellationToken cancellationToken = default); + /// + /// Adds environments to the end of a stack's import list. Imported environments are merged in order + /// per the ESC merge rules. The list of environments behaves as if it were the import list in an anonymous + /// environment. + /// + /// The name of the stack. + /// List of environments to add to the end of the stack's import list. + /// A cancellation token. + public abstract Task AddEnvironmentsAsync(string stackName, IEnumerable environments, CancellationToken cancellationToken = default); + + + /// + /// Removes environments from a stack's import list. + /// + /// The name of the stack. + /// The name of the environment to remove from the stack's configuration. + /// A cancellation token. + public abstract Task RemoveEnvironmentAsync(string stackName, string environment, CancellationToken cancellationToken = default); + /// /// Returns the value associated with the specified stack name and key, /// scoped to the Workspace. diff --git a/sdk/Pulumi.Automation/WorkspaceStack.cs b/sdk/Pulumi.Automation/WorkspaceStack.cs index 10e27836..764d37e5 100644 --- a/sdk/Pulumi.Automation/WorkspaceStack.cs +++ b/sdk/Pulumi.Automation/WorkspaceStack.cs @@ -274,6 +274,25 @@ public Task RemoveAllConfigAsync(IEnumerable keys, bool path, Cancellati public Task> RefreshConfigAsync(CancellationToken cancellationToken = default) => this.Workspace.RefreshConfigAsync(this.Name, cancellationToken); + + /// + /// Adds environments to the end of a stack's import list. Imported environments are merged in order + /// per the ESC merge rules. The list of environments behaves as if it were the import list in an anonymous + /// environment. + /// + /// List of environments to add to the end of the stack's import list. + /// A cancellation token. + public Task AddEnvironmentsAsync(IEnumerable environments, CancellationToken cancellationToken = default) + => this.Workspace.AddEnvironmentsAsync(this.Name, environments, cancellationToken); + + /// + /// Removes environments from a stack's import list. + /// + /// The name of the environment to remove from the stack's configuration. + /// A cancellation token. + public Task RemoveEnvironmentAsync(string environment, CancellationToken cancellationToken = default) + => this.Workspace.RemoveEnvironmentAsync(this.Name, environment, cancellationToken); + /// /// Creates or updates the resources in a stack by executing the program in the Workspace. /// diff --git a/sdk/Pulumi/Pulumi.xml b/sdk/Pulumi/Pulumi.xml index 4232e09e..657e9bde 100644 --- a/sdk/Pulumi/Pulumi.xml +++ b/sdk/Pulumi/Pulumi.xml @@ -711,6 +711,11 @@ The output properties of all resource objects in Pulumi have type . + + + for more details. + + for more details. @@ -2567,6 +2572,12 @@ MockCallArgs Invocation result, can be either a POCO or a dictionary bag. + + + Invoked when component resources (including instances of Stack) register their outputs + + MockRegisterResourceOutputsRequest + MockResourceArgs for use in NewResourceAsync @@ -2617,6 +2628,21 @@ Provider. + + + MockRegisterResourceOutputsRequest for use in RegisterOutputRequest + + + + + The URN of the resource of which the outputs are being registered + + + + + The outputs which have been registered by the resource + + Optional settings for . @@ -3777,6 +3803,15 @@ the gRPC target of the mapper service. + + Field number for the "args" field. + + + + the args passed to `pulumi import` for this conversion. Normally used to specifiy a state file to + import from. + + A ResourceImport specifies a resource to import. @@ -3854,6 +3889,22 @@ the gRPC target of the mapper service. + + Field number for the "loader_target" field. + + + + The target of a codegen.LoaderServer to use for loading schemas. + + + + Field number for the "args" field. + + + + the args passed to `pulumi convert` for this conversion. Normally used to specifiy a root file, or conversion options. + + Field number for the "diagnostics" field. @@ -4699,6 +4750,15 @@ The target of a codegen.LoaderServer to use for loading schemas. + + Field number for the "local_dependencies" field. + + + + local dependencies to use instead of using the package system. This is a map of package name to a local + path of a language specific artifact to use for the SDK for that package. + + Field number for the "diagnostics" field. @@ -4739,6 +4799,38 @@ The target of a codegen.LoaderServer to use for loading schemas. + + Field number for the "package_directory" field. + + + + the directory of a package to pack. + + + + Field number for the "version" field. + + + + the version to tag the artifact with. + + + + Field number for the "destination_directory" field. + + + + the directory to write the packed artifact to. + + + + Field number for the "artifact_path" field. + + + + the full path of the packed artifact. + + LanguageRuntime is the interface that the planning monitor uses to drive execution of an interpreter responsible @@ -4833,6 +4925,14 @@ The context of the server-side call handler being invoked. The response to send back to the client (wrapped by a task). + + + Pack packs a package into a language specific artifact. + + The request received from the client. + The context of the server-side call handler being invoked. + The response to send back to the client (wrapped by a task). + Client for LanguageRuntime @@ -5175,6 +5275,42 @@ The options for the call. The call object. + + + Pack packs a package into a language specific artifact. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The response received from the server. + + + + Pack packs a package into a language specific artifact. + + The request to send to the server. + The options for the call. + The response received from the server. + + + + Pack packs a package into a language specific artifact. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The call object. + + + + Pack packs a package into a language specific artifact. + + The request to send to the server. + The options for the call. + The call object. + Creates a new instance of client from given ClientBaseConfiguration. @@ -5244,6 +5380,15 @@ the URL of a server that can be used to download this plugin, if needed. + + Field number for the "checksums" field. + + + + a map of the checksums for the plugin, will be empty from old language runtimes. The keys should match + the os and architecture names used in pulumi releases, e.g. "darwin-amd64", "windows-arm64". + + PluginAttach is used to attach an already running plugin to the engine. @@ -5467,6 +5612,14 @@ the pluginDownloadURL of the provider to use when servicing this request. + + Field number for the "pluginChecksums" field. + + + + a map of checksums of the provider to use when servicing this request. + + Field number for the "project" field. @@ -6410,6 +6563,16 @@ the conversion key for the mapping being requested. + + Field number for the "provider" field. + + + + the optional provider key for the mapping being requested, if this is empty the provider should assume this + request is from an old engine from before GetMappings and should return it's "primary" mapping. If this is set + then the `provider` field in GetMappingResponse should be the same. + + GetMappingResponse returns convert plugin specific data for this provider. This will normally be human @@ -6432,6 +6595,34 @@ the conversion plugin specific data. + + + GetMappingsRequest allows providers to return ecosystem specific information without having to send back large data + blobs for provider mappings that the engine doesn't then need. + + + + Field number for the "key" field. + + + + the conversion key for the mapping being requested. + + + + + GetMappingsRequest returns a list of providers that this provider can provide mapping information for. + + + + Field number for the "providers" field. + + + + the provider keys this provider can supply mappings for. For example the Pulumi provider "terraform-template" + would return ["template"] for this. + + ResourceProvider is a service that understands how to create, read, update, or delete resources for types defined @@ -6601,6 +6792,16 @@ The context of the server-side call handler being invoked. The response to send back to the client (wrapped by a task). + + + GetMappings is an optional method that returns what mappings (if any) a provider supports. If a provider does not + implement this method the engine falls back to the old behaviour of just calling GetMapping without a name. + If this method is implemented than the engine will then call GetMapping only with the names returned from this method. + + The request received from the client. + The context of the server-side call handler being invoked. + The response to send back to the client (wrapped by a task). + Client for ResourceProvider @@ -7295,6 +7496,50 @@ The options for the call. The call object. + + + GetMappings is an optional method that returns what mappings (if any) a provider supports. If a provider does not + implement this method the engine falls back to the old behaviour of just calling GetMapping without a name. + If this method is implemented than the engine will then call GetMapping only with the names returned from this method. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The response received from the server. + + + + GetMappings is an optional method that returns what mappings (if any) a provider supports. If a provider does not + implement this method the engine falls back to the old behaviour of just calling GetMapping without a name. + If this method is implemented than the engine will then call GetMapping only with the names returned from this method. + + The request to send to the server. + The options for the call. + The response received from the server. + + + + GetMappings is an optional method that returns what mappings (if any) a provider supports. If a provider does not + implement this method the engine falls back to the old behaviour of just calling GetMapping without a name. + If this method is implemented than the engine will then call GetMapping only with the names returned from this method. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The call object. + + + + GetMappings is an optional method that returns what mappings (if any) a provider supports. If a provider does not + implement this method the engine falls back to the old behaviour of just calling GetMapping without a name. + If this method is implemented than the engine will then call GetMapping only with the names returned from this method. + + The request to send to the server. + The options for the call. + The call object. + Creates a new instance of client from given ClientBaseConfiguration. @@ -7437,6 +7682,14 @@ the server url of the provider to use when servicing this request. + + Field number for the "pluginChecksums" field. + + + + a map of checksums of the provider to use when servicing this request. + + Field number for the "sourcePosition" field. @@ -7663,6 +7916,14 @@ the server URL of the provider to use when servicing this request. + + Field number for the "pluginChecksums" field. + + + + a map of checksums expected for the provider plugin. + + Field number for the "retainOnDelete" field. @@ -7893,6 +8154,14 @@ an optional reference to the provider url to use for this invoke. + + Field number for the "pluginChecksums" field. + + + + a map of checksums expected for the provider plugin. + + Field number for the "sourcePosition" field. @@ -7978,6 +8247,231 @@ The column in the line + + Holder for reflection information generated from pulumi/testing/language.proto + + + File descriptor for pulumi/testing/language.proto + + + Field number for the "tests" field. + + + Field number for the "language_plugin_name" field. + + + Field number for the "language_plugin_target" field. + + + Field number for the "snapshot_directory" field. + + + Field number for the "temporary_directory" field. + + + Field number for the "core_sdk_directory" field. + + + Field number for the "token" field. + + + Field number for the "token" field. + + + Field number for the "test" field. + + + Field number for the "success" field. + + + Field number for the "messages" field. + + + Field number for the "stdout" field. + + + Field number for the "stderr" field. + + + + LanguageTest is the interface to the pulumi language test framework. This is _highly_ experimental and + currently subject to breaking changes without warning. + + + + Service descriptor + + + Base class for server-side implementations of LanguageTest + + + + GetLanguageTests returns a list of all the language tests. + + The request received from the client. + The context of the server-side call handler being invoked. + The response to send back to the client (wrapped by a task). + + + + PrepareLanguageTests prepares the engine to run language tests. It sets up a stable artifacts folder + (which should be .gitignore'd) and fills it with the core SDK artifact. + + The request received from the client. + The context of the server-side call handler being invoked. + The response to send back to the client (wrapped by a task). + + + + RunLanguageTest runs a single test of the language plugin. + + The request received from the client. + The context of the server-side call handler being invoked. + The response to send back to the client (wrapped by a task). + + + Client for LanguageTest + + + Creates a new client for LanguageTest + The channel to use to make remote calls. + + + Creates a new client for LanguageTest that uses a custom CallInvoker. + The callInvoker to use to make remote calls. + + + Protected parameterless constructor to allow creation of test doubles. + + + Protected constructor to allow creation of configured clients. + The client configuration. + + + + GetLanguageTests returns a list of all the language tests. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The response received from the server. + + + + GetLanguageTests returns a list of all the language tests. + + The request to send to the server. + The options for the call. + The response received from the server. + + + + GetLanguageTests returns a list of all the language tests. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The call object. + + + + GetLanguageTests returns a list of all the language tests. + + The request to send to the server. + The options for the call. + The call object. + + + + PrepareLanguageTests prepares the engine to run language tests. It sets up a stable artifacts folder + (which should be .gitignore'd) and fills it with the core SDK artifact. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The response received from the server. + + + + PrepareLanguageTests prepares the engine to run language tests. It sets up a stable artifacts folder + (which should be .gitignore'd) and fills it with the core SDK artifact. + + The request to send to the server. + The options for the call. + The response received from the server. + + + + PrepareLanguageTests prepares the engine to run language tests. It sets up a stable artifacts folder + (which should be .gitignore'd) and fills it with the core SDK artifact. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The call object. + + + + PrepareLanguageTests prepares the engine to run language tests. It sets up a stable artifacts folder + (which should be .gitignore'd) and fills it with the core SDK artifact. + + The request to send to the server. + The options for the call. + The call object. + + + + RunLanguageTest runs a single test of the language plugin. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The response received from the server. + + + + RunLanguageTest runs a single test of the language plugin. + + The request to send to the server. + The options for the call. + The response received from the server. + + + + RunLanguageTest runs a single test of the language plugin. + + The request to send to the server. + The initial metadata to send with the call. This parameter is optional. + An optional deadline for the call. The call will be cancelled if deadline is hit. + An optional token for canceling the call. + The call object. + + + + RunLanguageTest runs a single test of the language plugin. + + The request to send to the server. + The options for the call. + The call object. + + + Creates a new instance of client from given ClientBaseConfiguration. + + + Creates service definition that can be registered with a server + An object implementing the server-side handling logic. + + + Register service method with a service binder with or without implementation. Useful when customizing the service binding logic. + Note: this method is part of an experimental API that can change or be removed without any prior notice. + Service methods will be bound by calling AddMethod on this object. + An object implementing the server-side handling logic. + Holder for reflection information generated from pulumi/codegen/loader.proto