-
Notifications
You must be signed in to change notification settings - Fork 860
Add aspire destroy command for tearing down deployed environments #16097
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 22 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
e6edfa6
Add aspire destroy command for tearing down deployed environments
davidfowl a2e0559
Simplify destroy-prereq confirmation message
davidfowl 3a7c1b3
Enumerate Azure resources before destroying resource group
davidfowl 619ef46
Add DestroyCommand unit tests and pipeline wiring tests
davidfowl 0943758
Move confirmation prompts to environment-specific destroy steps
davidfowl 6cbd33a
Update deployment E2E tests to use aspire destroy for cleanup
davidfowl a6202c2
Fix review findings: non-interactive guard and step wiring
davidfowl 364bb12
Clear deployment state after successful destroy
davidfowl 5840dfc
Address review: fail-fast non-interactive guard and state cleanup docs
davidfowl 03dea07
Improve error message when compose file not found during destroy
davidfowl 02134e9
Surface stderr in compose down error messages
davidfowl 9f8e99c
Persist deployment state for Docker Compose and Helm
davidfowl c469926
Address manual CR feedback
davidfowl d8c1892
Clear all deployment state on full destroy via ClearAllStateAsync
davidfowl 3adc71e
Add deployment summary to destroy output
davidfowl 9ece783
Use ClearAllStateAsync for --clear-cache instead of raw File.Delete
davidfowl 23cd951
Add Azure destroy unit tests with mockable state manager
davidfowl 5c93ddd
Use persisted state for destroy operations and add Compose destroy tests
davidfowl 762d6b7
Introduce IHelmRunner abstraction and add Helm destroy tests
davidfowl b0d4cc4
Improve test quality: observable ARM mocks and deploy→destroy roundtrip
davidfowl 3f531d5
Rename PipelineOptions.Yes to SkipConfirmation
davidfowl 55aa8c9
Add API compat suppressions for new interface members
davidfowl 3c5d18e
Fix unused using in KubernetesEnvironmentExtensions
davidfowl d70f56f
Add Azure portal link to destroy summary
davidfowl 728e244
Extract AzurePortalUrls helper for shared portal URL generation
davidfowl c0c3b04
Address PR review feedback
davidfowl 99b8d87
Use sentence case for destroy status messages
davidfowl a583f50
Add regression test for Helm uninstall failure preserving state
davidfowl 38e5450
Address remaining review feedback
davidfowl c6a7600
Fix compose destroy failing on stale build contexts
davidfowl 052bb72
Wait for Azure resource group deletion to complete
davidfowl 2759774
Fix Helm double task completion and improve Azure delete messaging
davidfowl 9125cd1
Add deletion status to destroy summary
davidfowl 72fe4d7
Show portal URL inline in destroy status summary
davidfowl 84ce8cf
Address review feedback from eerhardt
davidfowl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| // Licensed to the .NET Foundation under one or more agreements. | ||
| // The .NET Foundation licenses this file to you under the MIT license. | ||
|
|
||
| using System.CommandLine; | ||
| using Aspire.Cli.Configuration; | ||
| using Aspire.Cli.DotNet; | ||
| using Aspire.Cli.Interaction; | ||
| using Aspire.Cli.Projects; | ||
| using Aspire.Cli.Resources; | ||
| using Aspire.Cli.Telemetry; | ||
| using Aspire.Cli.Utils; | ||
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.Logging; | ||
| using Spectre.Console; | ||
|
|
||
| namespace Aspire.Cli.Commands; | ||
|
|
||
| internal sealed class DestroyCommand : PipelineCommandBase | ||
| { | ||
| internal override HelpGroup HelpGroup => HelpGroup.Deployment; | ||
|
|
||
| private readonly Option<bool> _yesOption; | ||
|
|
||
| public DestroyCommand(IDotNetCliRunner runner, IInteractionService interactionService, IProjectLocator projectLocator, AspireCliTelemetry telemetry, IFeatures features, ICliUpdateNotifier updateNotifier, CliExecutionContext executionContext, ICliHostEnvironment hostEnvironment, IAppHostProjectFactory projectFactory, IConfiguration configuration, ILogger<DestroyCommand> logger, IAnsiConsole ansiConsole) | ||
| : base("destroy", DestroyCommandStrings.Description, runner, interactionService, projectLocator, telemetry, features, updateNotifier, executionContext, hostEnvironment, projectFactory, configuration, logger, ansiConsole) | ||
| { | ||
| _yesOption = new Option<bool>("--yes", "-y") | ||
| { | ||
| Description = DestroyCommandStrings.YesOptionDescription | ||
| }; | ||
| Options.Add(_yesOption); | ||
| } | ||
|
|
||
| protected override string OperationCompletedPrefix => DestroyCommandStrings.OperationCompletedPrefix; | ||
| protected override string OperationFailedPrefix => DestroyCommandStrings.OperationFailedPrefix; | ||
| protected override string GetOutputPathDescription() => DestroyCommandStrings.OutputPathArgumentDescription; | ||
|
|
||
| protected override Task<string[]> GetRunArgumentsAsync(string? fullyQualifiedOutputPath, string[] unmatchedTokens, ParseResult parseResult, CancellationToken cancellationToken) | ||
| { | ||
| var baseArgs = new List<string> { "--operation", "publish", "--step", "destroy" }; | ||
|
|
||
| if (fullyQualifiedOutputPath != null) | ||
| { | ||
| baseArgs.AddRange(["--output-path", fullyQualifiedOutputPath]); | ||
| } | ||
|
|
||
| var yes = parseResult.GetValue(_yesOption); | ||
| if (yes) | ||
| { | ||
| baseArgs.AddRange(["--yes", "true"]); | ||
| } | ||
|
|
||
| var logLevel = parseResult.GetValue(s_logLevelOption); | ||
| if (!string.IsNullOrEmpty(logLevel)) | ||
| { | ||
| baseArgs.AddRange(["--log-level", logLevel!]); | ||
| } | ||
|
|
||
| var includeExceptionDetails = parseResult.GetValue(s_includeExceptionDetailsOption); | ||
| if (includeExceptionDetails) | ||
| { | ||
| baseArgs.AddRange(["--include-exception-details", "true"]); | ||
| } | ||
|
|
||
| var environment = parseResult.GetValue(s_environmentOption); | ||
| if (!string.IsNullOrEmpty(environment)) | ||
| { | ||
| baseArgs.AddRange(["--environment", environment!]); | ||
| } | ||
|
|
||
| baseArgs.AddRange(unmatchedTokens); | ||
|
|
||
| return Task.FromResult<string[]>([.. baseArgs]); | ||
| } | ||
|
|
||
| protected override string GetCanceledMessage() => DestroyCommandStrings.DestroyCanceled; | ||
|
|
||
| protected override string GetProgressMessage(ParseResult parseResult) | ||
| { | ||
| return "Executing step destroy"; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
src/Aspire.Cli/Resources/DestroyCommandStrings.Designer.cs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| <?xml version="1.0" encoding="utf-8"?> | ||
| <root> | ||
| <!-- | ||
| Microsoft ResX Schema | ||
|
|
||
| Version 2.0 | ||
|
|
||
| The primary goals of this format is to allow a simple XML format | ||
| that is mostly human readable. The generation and parsing of the | ||
| various data types are done through the TypeConverter classes | ||
| associated with the data types. | ||
|
|
||
| Example: | ||
|
|
||
| ... ado.net/XML headers & schema ... | ||
| <resheader name="resmimetype">text/microsoft-resx</resheader> | ||
| <resheader name="version">2.0</resheader> | ||
| <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | ||
| <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | ||
| <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | ||
| <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | ||
| <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | ||
| <value>[base64 mime encoded serialized .NET Framework object]</value> | ||
| </data> | ||
| <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | ||
| <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | ||
| <comment>This is a comment</comment> | ||
| </data> | ||
|
|
||
| There are any number of "resheader" rows that contain simple | ||
| name/value pairs. | ||
|
|
||
| Each data row contains a name, and value. The row also contains a | ||
| type or mimetype. Type corresponds to a .NET class that support | ||
| text/value conversion through the TypeConverter architecture. | ||
| Classes that don't support this are serialized and stored with the | ||
| mimetype set. | ||
|
|
||
| The mimetype is used for serialized objects, and tells the | ||
| ResXResourceReader how to depersist the object. This is currently not | ||
| extensible. For a given mimetype the value must be set accordingly: | ||
|
|
||
| Note - application/x-microsoft.net.object.binary.base64 is the format | ||
| that the ResXResourceWriter will generate, however the reader can | ||
| read any of the formats listed below. | ||
|
|
||
| mimetype: application/x-microsoft.net.object.binary.base64 | ||
| value : The object must be serialized with | ||
| : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter | ||
| : and then encoded with base64 encoding. | ||
|
|
||
| mimetype: application/x-microsoft.net.object.soap.base64 | ||
| value : The object must be serialized with | ||
| : System.Runtime.Serialization.Formatters.Soap.SoapFormatter | ||
| : and then encoded with base64 encoding. | ||
|
|
||
| mimetype: application/x-microsoft.net.object.bytearray.base64 | ||
| value : The object must be serialized into a byte array | ||
| : using a System.ComponentModel.TypeConverter | ||
| : and then encoded with base64 encoding. | ||
| --> | ||
| <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | ||
| <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> | ||
| <xsd:element name="root" msdata:IsDataSet="true"> | ||
| <xsd:complexType> | ||
| <xsd:choice maxOccurs="unbounded"> | ||
| <xsd:element name="metadata"> | ||
| <xsd:complexType> | ||
| <xsd:sequence> | ||
| <xsd:element name="value" type="xsd:string" minOccurs="0" /> | ||
| </xsd:sequence> | ||
| <xsd:attribute name="name" use="required" type="xsd:string" /> | ||
| <xsd:attribute name="type" type="xsd:string" /> | ||
| <xsd:attribute name="mimetype" type="xsd:string" /> | ||
| <xsd:attribute ref="xml:space" /> | ||
| </xsd:complexType> | ||
| </xsd:element> | ||
| <xsd:element name="assembly"> | ||
| <xsd:complexType> | ||
| <xsd:attribute name="alias" type="xsd:string" /> | ||
| <xsd:attribute name="name" type="xsd:string" /> | ||
| </xsd:complexType> | ||
| </xsd:element> | ||
| <xsd:element name="data"> | ||
| <xsd:complexType> | ||
| <xsd:sequence> | ||
| <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | ||
| <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | ||
| </xsd:sequence> | ||
| <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> | ||
| <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | ||
| <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | ||
| <xsd:attribute ref="xml:space" /> | ||
| </xsd:complexType> | ||
| </xsd:element> | ||
| <xsd:element name="resheader"> | ||
| <xsd:complexType> | ||
| <xsd:sequence> | ||
| <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | ||
| </xsd:sequence> | ||
| <xsd:attribute name="name" type="xsd:string" use="required" /> | ||
| </xsd:complexType> | ||
| </xsd:element> | ||
| </xsd:choice> | ||
| </xsd:complexType> | ||
| </xsd:element> | ||
| </xsd:schema> | ||
| <resheader name="resmimetype"> | ||
| <value>text/microsoft-resx</value> | ||
| </resheader> | ||
| <resheader name="version"> | ||
| <value>2.0</value> | ||
| </resheader> | ||
| <resheader name="reader"> | ||
| <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||
| </resheader> | ||
| <resheader name="writer"> | ||
| <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||
| </resheader> | ||
| <data name="Description" xml:space="preserve"> | ||
| <value>Destroy a previously deployed AppHost environment (Preview)</value> | ||
| </data> | ||
| <data name="OutputPathArgumentDescription" xml:space="preserve"> | ||
| <value>The output path containing the deployment artifacts to destroy</value> | ||
| </data> | ||
| <data name="DestroyCanceled" xml:space="preserve"> | ||
| <value>The destroy operation was canceled.</value> | ||
| </data> | ||
| <data name="OperationCompletedPrefix" xml:space="preserve"> | ||
| <value>DESTROY COMPLETED</value> | ||
| </data> | ||
| <data name="OperationFailedPrefix" xml:space="preserve"> | ||
| <value>DESTROY FAILED</value> | ||
| </data> | ||
davidfowl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| <data name="YesOptionDescription" xml:space="preserve"> | ||
| <value>Skip the confirmation prompt and proceed with the destroy operation</value> | ||
| </data> | ||
| </root> | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.