diff --git a/src/AWS.Deploy.CLI/AWSUtilities.cs b/src/AWS.Deploy.CLI/AWSUtilities.cs index 4e7cc7d04..d0c6a47b8 100644 --- a/src/AWS.Deploy.CLI/AWSUtilities.cs +++ b/src/AWS.Deploy.CLI/AWSUtilities.cs @@ -80,7 +80,7 @@ await CanLoadCredentials(lastUsedCredentials)) var sharedCredentials = new SharedCredentialsFile(); if (sharedCredentials.ListProfileNames().Count == 0) { - throw new NoAWSCredentialsFoundException("Unable to resolve AWS credentials to access AWS."); + throw new NoAWSCredentialsFoundException(DeployToolErrorCode.UnableToResolveAWSCredentials, "Unable to resolve AWS credentials to access AWS."); } var selectedProfileName = _consoleUtilities.AskUserToChoose(sharedCredentials.ListProfileNames(), "Select AWS Credentials Profile", null); @@ -91,7 +91,7 @@ await CanLoadCredentials(lastUsedCredentials)) return selectedProfileCredentials; } - throw new NoAWSCredentialsFoundException($"Unable to create AWS credentials for profile {selectedProfileName}."); + throw new NoAWSCredentialsFoundException(DeployToolErrorCode.UnableToCreateAWSCredentials, $"Unable to create AWS credentials for profile {selectedProfileName}."); } var credentials = await Resolve(); diff --git a/src/AWS.Deploy.CLI/CommandReturnCodes.cs b/src/AWS.Deploy.CLI/CommandReturnCodes.cs index 3102ead26..78fe2d275 100644 --- a/src/AWS.Deploy.CLI/CommandReturnCodes.cs +++ b/src/AWS.Deploy.CLI/CommandReturnCodes.cs @@ -16,8 +16,8 @@ public class CommandReturnCodes /// exception was thrown. This usually means there is an intermittent io problem /// or bug in the code base. /// - /// Unexpected exception are any exception not marked by - /// + /// Unexpected exceptions are any exception that do not inherit from + /// /// public const int UNHANDLED_EXCEPTION = -1; /// @@ -25,8 +25,8 @@ public class CommandReturnCodes /// configuration or system configuration problem. For example, a required /// dependency like Docker is not installed. /// - /// Expected problems are usually indicated by throwing an exception that is - /// decorated with + /// Expected problems are usually indicated by throwing an exception that + /// inherits from /// public const int USER_ERROR = 1; /// diff --git a/src/AWS.Deploy.CLI/Commands/DeleteDeploymentCommand.cs b/src/AWS.Deploy.CLI/Commands/DeleteDeploymentCommand.cs index baf5af13a..29bd42ad1 100644 --- a/src/AWS.Deploy.CLI/Commands/DeleteDeploymentCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/DeleteDeploymentCommand.cs @@ -126,10 +126,10 @@ private async Task WaitForStackDelete(string stackName) if (stack.StackStatus.IsFailed()) { - throw new FailedToDeleteException($"The stack {stackName} is in a failed state. You may need to delete it from the AWS Console."); + throw new FailedToDeleteException(DeployToolErrorCode.FailedToDeleteStack, $"The stack {stackName} is in a failed state. You may need to delete it from the AWS Console."); } - throw new FailedToDeleteException($"Failed to delete {stackName} stack: {stack.StackStatus}"); + throw new FailedToDeleteException(DeployToolErrorCode.FailedToDeleteStack, $"Failed to delete {stackName} stack: {stack.StackStatus}"); } private async Task StabilizeStack(string stackName) diff --git a/src/AWS.Deploy.CLI/Commands/DeployCommand.cs b/src/AWS.Deploy.CLI/Commands/DeployCommand.cs index 8ea3fafe7..5f8be7ab2 100644 --- a/src/AWS.Deploy.CLI/Commands/DeployCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/DeployCommand.cs @@ -179,7 +179,7 @@ private void DisplayOutputResources(List displayedResourc if (!compatibleApplications.Any(app => app.StackName.Equals(deployedApplication.StackName, StringComparison.Ordinal))) { var errorMessage = $"{deployedApplication.StackName} already exists as a Cloudformation stack but a compatible recommendation to perform a redeployment was not found"; - throw new FailedToFindCompatibleRecipeException(errorMessage); + throw new FailedToFindCompatibleRecipeException(DeployToolErrorCode.CompatibleRecommendationForRedeploymentNotFound, errorMessage); } // preset settings for deployment based on last deployment. @@ -219,7 +219,7 @@ public async Task EvaluateSystemCapabilities(Recommendation selectedRecommendati } if (systemCapabilities.Any()) - throw new MissingSystemCapabilityException(missingCapabilitiesMessage); + throw new MissingSystemCapabilityException(DeployToolErrorCode.MissingSystemCapabilities, missingCapabilitiesMessage); } /// @@ -253,7 +253,7 @@ private async Task> GenerateDeploymentRecommendations(Orche if (!recommendations.Any()) { var errorMessage = $"Could not find any deployment recipe located inside '{deploymentProjectPath}' that can be used for deployment of the target application"; - throw new FailedToGenerateAnyRecommendations(errorMessage); + throw new FailedToGenerateAnyRecommendations(DeployToolErrorCode.NoDeploymentRecipesFound, errorMessage); } } else @@ -262,7 +262,7 @@ private async Task> GenerateDeploymentRecommendations(Orche if (!recommendations.Any()) { var errorMessage = "There are no compatible deployment recommendations for this application."; - throw new FailedToGenerateAnyRecommendations(errorMessage); + throw new FailedToGenerateAnyRecommendations(DeployToolErrorCode.NoCompatibleDeploymentRecipesFound, errorMessage); } } return recommendations; @@ -276,7 +276,7 @@ private async Task GetSelectedRecommendationFromPreviousDeployme if (selectedRecommendation == null) { var errorMessage = $"{deployedApplication.StackName} already exists as a Cloudformation stack but a compatible recommendation used to perform a re-deployment was not found."; - throw new FailedToFindCompatibleRecipeException(errorMessage); + throw new FailedToFindCompatibleRecipeException(DeployToolErrorCode.CompatibleRecommendationForRedeploymentNotFound, errorMessage); } if (!string.IsNullOrEmpty(deploymentSettingRecipeId) && !string.Equals(deploymentSettingRecipeId, selectedRecommendation.Recipe.Id, StringComparison.InvariantCultureIgnoreCase)) { @@ -286,7 +286,7 @@ private async Task GetSelectedRecommendationFromPreviousDeployme { errorMessage += Environment.NewLine + $"The original deployment recipe ID was {deployedApplication.RecipeId} and the current deployment recipe ID is {deploymentSettingRecipeId}"; } - throw new InvalidUserDeploymentSettingsException(errorMessage.Trim()); + throw new InvalidUserDeploymentSettingsException(DeployToolErrorCode.StackCreatedFromDifferentDeploymentRecommendation, errorMessage.Trim()); } selectedRecommendation = selectedRecommendation.ApplyPreviousSettings(existingCloudApplicationMetadata.Settings); @@ -351,7 +351,7 @@ private async Task GetDeploymentProjectRecipeId(string deploymentProject } catch (Exception ex) { - throw new FailedToFindDeploymentProjectRecipeIdException($"Failed to find a recipe ID for the deployment project located at {deploymentProjectPath}", ex); + throw new FailedToFindDeploymentProjectRecipeIdException(DeployToolErrorCode.FailedToFindDeploymentProjectRecipeId, $"Failed to find a recipe ID for the deployment project located at {deploymentProjectPath}", ex); } } @@ -389,13 +389,13 @@ private void ConfigureDeploymentFromConfigFile(Recommendation recommendation, Us settingValue = double.Parse(optionSettingValue); break; default: - throw new InvalidOverrideValueException($"Invalid value {optionSettingValue} for option setting item {optionSettingJsonPath}"); + throw new InvalidOverrideValueException(DeployToolErrorCode.InvalidValueForOptionSettingItem, $"Invalid value {optionSettingValue} for option setting item {optionSettingJsonPath}"); } } catch (Exception exception) { _toolInteractiveService.WriteDebugLine(exception.PrettyPrint()); - throw new InvalidOverrideValueException($"Invalid value {optionSettingValue} for option setting item {optionSettingJsonPath}"); + throw new InvalidOverrideValueException(DeployToolErrorCode.InvalidValueForOptionSettingItem, $"Invalid value {optionSettingValue} for option setting item {optionSettingJsonPath}"); } optionSetting.SetValueOverride(settingValue); @@ -423,7 +423,7 @@ private void ConfigureDeploymentFromConfigFile(Recommendation recommendation, Us { errorMessage += result.ValidationFailedMessage + Environment.NewLine; } - throw new InvalidUserDeploymentSettingsException(errorMessage.Trim()); + throw new InvalidUserDeploymentSettingsException(DeployToolErrorCode.DeploymentConfigurationNeedsAdjusting, errorMessage.Trim()); } private void SetDeploymentBundleOptionSetting(Recommendation recommendation, string optionSettingId, object settingValue) @@ -459,7 +459,7 @@ private string GetCloudApplicationName(string? stackName, UserDeploymentSettings return stackName; PrintInvalidStackNameMessage(); - throw new InvalidCliArgumentException("Found invalid CLI arguments"); + throw new InvalidCliArgumentException(DeployToolErrorCode.InvalidCliArguments, "Found invalid CLI arguments"); } if (!string.IsNullOrEmpty(userDeploymentSettings?.StackName)) @@ -468,14 +468,14 @@ private string GetCloudApplicationName(string? stackName, UserDeploymentSettings return userDeploymentSettings.StackName; PrintInvalidStackNameMessage(); - throw new InvalidUserDeploymentSettingsException("Please provide a valid stack name and try again."); + throw new InvalidUserDeploymentSettingsException(DeployToolErrorCode.UserDeploymentInvalidStackName, "Please provide a valid stack name and try again."); } if (_toolInteractiveService.DisableInteractive) { var message = "The \"--silent\" CLI argument can only be used if a CDK stack name is provided either via the CLI argument \"--stack-name\" or through a deployment-settings file. " + "Please provide a stack name and try again"; - throw new InvalidCliArgumentException(message); + throw new InvalidCliArgumentException(DeployToolErrorCode.SilentArgumentNeedsStackNameArgument, message); } return AskUserForCloudApplicationName(_session.ProjectDefinition, deployedApplications); } @@ -498,7 +498,7 @@ private Recommendation GetSelectedRecommendation(UserDeploymentSettings? userDep "deployement-settings file or if a path to a custom CDK deployment project is provided via the '--deployment-project' CLI argument." + $"{Environment.NewLine}Please provide a deployment recipe and try again"; - throw new InvalidCliArgumentException(message); + throw new InvalidCliArgumentException(DeployToolErrorCode.SilentArgumentNeedsDeploymentRecipe, message); } return _consoleUtilities.AskToChooseRecommendation(recommendations); } @@ -506,7 +506,7 @@ private Recommendation GetSelectedRecommendation(UserDeploymentSettings? userDep var selectedRecommendation = recommendations.FirstOrDefault(x => x.Recipe.Id.Equals(deploymentSettingsRecipeId, StringComparison.Ordinal)); if (selectedRecommendation == null) { - throw new InvalidUserDeploymentSettingsException($"The user deployment settings provided contains an invalid value for the property '{nameof(userDeploymentSettings.RecipeId)}'."); + throw new InvalidUserDeploymentSettingsException(DeployToolErrorCode.InvalidPropertyValueForUserDeployment, $"The user deployment settings provided contains an invalid value for the property '{nameof(userDeploymentSettings.RecipeId)}'."); } _toolInteractiveService.WriteLine(); @@ -602,7 +602,7 @@ private async Task CreateDeploymentBundle(Orchestrator orchestrator, Recommendat var errorMessage = "Failed to build Docker Image." + Environment.NewLine; errorMessage += "Docker builds usually fail due to executing them from a working directory that is incompatible with the Dockerfile." + Environment.NewLine; errorMessage += "Specify a valid Docker execution directory as part of the deployment settings file and try again."; - throw new DockerBuildFailedException(errorMessage); + throw new DockerBuildFailedException(DeployToolErrorCode.DockerBuildFailed, errorMessage); } _toolInteractiveService.WriteLine(string.Empty); @@ -622,7 +622,7 @@ private async Task CreateDeploymentBundle(Orchestrator orchestrator, Recommendat } else { - throw new FailedToCreateDeploymentBundleException("Failed to create a deployment bundle"); + throw new FailedToCreateDeploymentBundleException(DeployToolErrorCode.FailedToCreateContainerDeploymentBundle, "Failed to create a deployment bundle"); } } } @@ -630,7 +630,7 @@ private async Task CreateDeploymentBundle(Orchestrator orchestrator, Recommendat { var dotnetPublishDeploymentBundleResult = await orchestrator.CreateDotnetPublishDeploymentBundle(selectedRecommendation); if (!dotnetPublishDeploymentBundleResult) - throw new FailedToCreateDeploymentBundleException("Failed to create a deployment bundle"); + throw new FailedToCreateDeploymentBundleException(DeployToolErrorCode.FailedToCreateDotnetPublishDeploymentBundle, "Failed to create a deployment bundle"); } } diff --git a/src/AWS.Deploy.CLI/Commands/GenerateDeploymentProjectCommand.cs b/src/AWS.Deploy.CLI/Commands/GenerateDeploymentProjectCommand.cs index 6e159b05c..2dee18bd2 100644 --- a/src/AWS.Deploy.CLI/Commands/GenerateDeploymentProjectCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/GenerateDeploymentProjectCommand.cs @@ -80,7 +80,7 @@ public async Task ExecuteAsync(string saveCdkDirectoryPath, string projectDispla if (newDirectoryCreated) _directoryManager.Delete(saveCdkDirectoryPath); errorMessage = $"Failed to generate deployment project.{Environment.NewLine}{errorMessage}"; - throw new InvalidSaveDirectoryForCdkProject(errorMessage.Trim()); + throw new InvalidSaveDirectoryForCdkProject(DeployToolErrorCode.InvalidSaveDirectoryForCdkProject, errorMessage.Trim()); } var directoryUnderSourceControl = await IsDirectoryUnderSourceControl(saveCdkDirectoryPath); @@ -122,7 +122,7 @@ private async Task> GenerateRecommendationsToSaveDeployment var recommendations = await orchestrator.GenerateRecommendationsToSaveDeploymentProject(); if (recommendations.Count == 0) { - throw new FailedToGenerateAnyRecommendations("The project you are trying to deploy is currently not supported."); + throw new FailedToGenerateAnyRecommendations(DeployToolErrorCode.DeploymentProjectNotSupported, "The project you are trying to deploy is currently not supported."); } return recommendations; } diff --git a/src/AWS.Deploy.CLI/Commands/ServerModeCommand.cs b/src/AWS.Deploy.CLI/Commands/ServerModeCommand.cs index 1310d42a4..52cc2fb76 100644 --- a/src/AWS.Deploy.CLI/Commands/ServerModeCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/ServerModeCommand.cs @@ -39,7 +39,7 @@ public ServerModeCommand(IToolInteractiveService interactiveService, int port, i IEncryptionProvider encryptionProvider = CreateEncryptionProvider(); if (IsPortInUse(_port)) - throw new TcpPortInUseException("The port you have selected is currently in use by another process."); + throw new TcpPortInUseException(DeployToolErrorCode.TcpPortInUse, "The port you have selected is currently in use by another process."); var url = $"http://localhost:{_port}"; diff --git a/src/AWS.Deploy.CLI/Commands/TypeHints/BeanstalkApplicationCommand.cs b/src/AWS.Deploy.CLI/Commands/TypeHints/BeanstalkApplicationCommand.cs index 34c58d383..603ec3c75 100644 --- a/src/AWS.Deploy.CLI/Commands/TypeHints/BeanstalkApplicationCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/TypeHints/BeanstalkApplicationCommand.cs @@ -57,7 +57,7 @@ public async Task Execute(Recommendation recommendation, OptionSettingIt return new BeanstalkApplicationTypeHintResponse( userResponse.CreateNew, userResponse.SelectedOption?.ApplicationName ?? userResponse.NewName - ?? throw new UserPromptForNameReturnedNullException("The user response for a new application name was null.") + ?? throw new UserPromptForNameReturnedNullException(DeployToolErrorCode.BeanstalkAppPromptForNameReturnedNull, "The user response for a new application name was null.") ); } } diff --git a/src/AWS.Deploy.CLI/Commands/TypeHints/BeanstalkEnvironmentCommand.cs b/src/AWS.Deploy.CLI/Commands/TypeHints/BeanstalkEnvironmentCommand.cs index d779b06a4..5aa3f67fb 100644 --- a/src/AWS.Deploy.CLI/Commands/TypeHints/BeanstalkEnvironmentCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/TypeHints/BeanstalkEnvironmentCommand.cs @@ -48,7 +48,7 @@ public async Task Execute(Recommendation recommendation, OptionSettingIt askNewName: true, defaultNewName: currentValue.ToString() ?? ""); return userResponse.SelectedOption ?? userResponse.NewName - ?? throw new UserPromptForNameReturnedNullException("The user response for a new environment name was null."); + ?? throw new UserPromptForNameReturnedNullException(DeployToolErrorCode.BeanstalkEnvPromptForNameReturnedNull, "The user response for a new environment name was null."); } } } diff --git a/src/AWS.Deploy.CLI/Commands/TypeHints/DockerBuildArgsCommand.cs b/src/AWS.Deploy.CLI/Commands/TypeHints/DockerBuildArgsCommand.cs index 226a87e37..1f4596ff4 100644 --- a/src/AWS.Deploy.CLI/Commands/TypeHints/DockerBuildArgsCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/TypeHints/DockerBuildArgsCommand.cs @@ -47,7 +47,7 @@ public void OverrideValue(Recommendation recommendation, string dockerBuildArgs) { var resultString = ValidateBuildArgs(dockerBuildArgs); if (!string.IsNullOrEmpty(resultString)) - throw new InvalidOverrideValueException(resultString); + throw new InvalidOverrideValueException(DeployToolErrorCode.InvalidDockerBuildArgs, resultString); recommendation.DeploymentBundle.DockerBuildArgs = dockerBuildArgs; } diff --git a/src/AWS.Deploy.CLI/Commands/TypeHints/DockerExecutionDirectoryCommand.cs b/src/AWS.Deploy.CLI/Commands/TypeHints/DockerExecutionDirectoryCommand.cs index 68a8cfb95..6e12f07bf 100644 --- a/src/AWS.Deploy.CLI/Commands/TypeHints/DockerExecutionDirectoryCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/TypeHints/DockerExecutionDirectoryCommand.cs @@ -48,7 +48,7 @@ public void OverrideValue(Recommendation recommendation, string executionDirecto { var resultString = ValidateExecutionDirectory(executionDirectory); if (!string.IsNullOrEmpty(resultString)) - throw new InvalidOverrideValueException(resultString); + throw new InvalidOverrideValueException(DeployToolErrorCode.InvalidDockerExecutionDirectory, resultString); recommendation.DeploymentBundle.DockerExecutionDirectory = executionDirectory; } diff --git a/src/AWS.Deploy.CLI/Commands/TypeHints/DotnetPublishArgsCommand.cs b/src/AWS.Deploy.CLI/Commands/TypeHints/DotnetPublishArgsCommand.cs index 67e514cee..cded90331 100644 --- a/src/AWS.Deploy.CLI/Commands/TypeHints/DotnetPublishArgsCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/TypeHints/DotnetPublishArgsCommand.cs @@ -48,7 +48,7 @@ public void OverrideValue(Recommendation recommendation, string publishArgs) { var resultString = ValidateDotnetPublishArgs(publishArgs); if (!string.IsNullOrEmpty(resultString)) - throw new InvalidOverrideValueException(resultString); + throw new InvalidOverrideValueException(DeployToolErrorCode.InvalidDotnetPublishArgs, resultString); recommendation.DeploymentBundle.DotnetPublishAdditionalBuildArguments = publishArgs.Replace("\"", "\"\""); } diff --git a/src/AWS.Deploy.CLI/Commands/TypeHints/EC2KeyPairCommand.cs b/src/AWS.Deploy.CLI/Commands/TypeHints/EC2KeyPairCommand.cs index fe762d988..c5fb5795d 100644 --- a/src/AWS.Deploy.CLI/Commands/TypeHints/EC2KeyPairCommand.cs +++ b/src/AWS.Deploy.CLI/Commands/TypeHints/EC2KeyPairCommand.cs @@ -68,7 +68,7 @@ public async Task Execute(Recommendation recommendation, OptionSettingIt else { settingValue = userResponse.SelectedOption?.KeyName ?? userResponse.NewName ?? - throw new UserPromptForNameReturnedNullException("The user prompt for a new EC2 Key Pair name was null or empty."); + throw new UserPromptForNameReturnedNullException(DeployToolErrorCode.EC2KeyPairPromptForNameReturnedNull, "The user prompt for a new EC2 Key Pair name was null or empty."); } if (userResponse.CreateNew && !string.IsNullOrEmpty(userResponse.NewName)) diff --git a/src/AWS.Deploy.CLI/Exceptions.cs b/src/AWS.Deploy.CLI/Exceptions.cs index 88f9df242..d33519562 100644 --- a/src/AWS.Deploy.CLI/Exceptions.cs +++ b/src/AWS.Deploy.CLI/Exceptions.cs @@ -9,79 +9,71 @@ namespace AWS.Deploy.CLI /// /// Throw if no AWS credentials were found. /// - [AWSDeploymentExpectedException] - public class NoAWSCredentialsFoundException : Exception + public class NoAWSCredentialsFoundException : DeployToolException { - public NoAWSCredentialsFoundException(string message, Exception? innerException = null) : base(message, innerException) { } + public NoAWSCredentialsFoundException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if Delete Command is unable to delete /// the specified stack /// - [AWSDeploymentExpectedException ] - public class FailedToDeleteException : Exception + public class FailedToDeleteException : DeployToolException { - public FailedToDeleteException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToDeleteException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if Deploy Command is unable to find a target to deploy. /// Currently, this is limited to .csproj or .fsproj files. /// - [AWSDeploymentExpectedException] - public class FailedToFindDeployableTargetException : Exception + public class FailedToFindDeployableTargetException : DeployToolException { - public FailedToFindDeployableTargetException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToFindDeployableTargetException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if prompting the user for a name returns a null value. /// - [AWSDeploymentExpectedException] - public class UserPromptForNameReturnedNullException : Exception + public class UserPromptForNameReturnedNullException : DeployToolException { - public UserPromptForNameReturnedNullException(string message, Exception? innerException = null) : base(message, innerException) { } + public UserPromptForNameReturnedNullException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if the system capabilities were not provided. /// - [AWSDeploymentExpectedException] - public class SystemCapabilitiesNotProvidedException : Exception + public class SystemCapabilitiesNotProvidedException : DeployToolException { - public SystemCapabilitiesNotProvidedException(string message, Exception? innerException = null) : base(message, innerException) { } + public SystemCapabilitiesNotProvidedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if TCP port is in use by another process. /// - [AWSDeploymentExpectedException] - public class TcpPortInUseException : Exception + public class TcpPortInUseException : DeployToolException { - public TcpPortInUseException(string message, Exception? innerException = null) : base(message, innerException) { } + public TcpPortInUseException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if unable to find a compatible recipe. /// - [AWSDeploymentExpectedException] - public class FailedToFindCompatibleRecipeException : Exception + public class FailedToFindCompatibleRecipeException : DeployToolException { - public FailedToFindCompatibleRecipeException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToFindCompatibleRecipeException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if the directory specified to save the CDK deployment project is invalid. /// - [AWSDeploymentExpectedException] - public class InvalidSaveDirectoryForCdkProject : Exception + public class InvalidSaveDirectoryForCdkProject : DeployToolException { - public InvalidSaveDirectoryForCdkProject(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidSaveDirectoryForCdkProject(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } - public class FailedToFindDeploymentProjectRecipeIdException : Exception + public class FailedToFindDeploymentProjectRecipeIdException : DeployToolException { - public FailedToFindDeploymentProjectRecipeIdException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToFindDeploymentProjectRecipeIdException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } } diff --git a/src/AWS.Deploy.CLI/ServerMode/Controllers/DeploymentController.cs b/src/AWS.Deploy.CLI/ServerMode/Controllers/DeploymentController.cs index 6f19b7ea3..7967594a7 100644 --- a/src/AWS.Deploy.CLI/ServerMode/Controllers/DeploymentController.cs +++ b/src/AWS.Deploy.CLI/ServerMode/Controllers/DeploymentController.cs @@ -68,8 +68,8 @@ IHubContext hubContext public async Task StartDeploymentSession(StartDeploymentSessionInput input) { var output = new StartDeploymentSessionOutput( - Guid.NewGuid().ToString() - ); + Guid.NewGuid().ToString() + ); var serviceProvider = CreateSessionServiceProvider(output.SessionId, input.AWSRegion); var awsResourceQueryer = serviceProvider.GetRequiredService(); @@ -482,7 +482,21 @@ public IActionResult GetDeploymentStatus(string sessionId) else if (state.DeploymentTask.IsCompleted && state.DeploymentTask.Status == TaskStatus.RanToCompletion) output.Status = DeploymentStatus.Success; else if (state.DeploymentTask.IsCompleted && state.DeploymentTask.Status == TaskStatus.Faulted) + { output.Status = DeploymentStatus.Error; + if (state.DeploymentTask.Exception != null) + { + var innerException = state.DeploymentTask.Exception.InnerException; + if (innerException is DeployToolException deployToolException) + { + output.Exception = new DeployToolExceptionSummary(deployToolException.ErrorCode.ToString(), deployToolException.Message); + } + else + { + output.Exception = new DeployToolExceptionSummary(DeployToolErrorCode.UnexpectedError.ToString(), innerException?.Message ?? string.Empty); + } + } + } else output.Status = DeploymentStatus.Executing; diff --git a/src/AWS.Deploy.CLI/ServerMode/Exceptions.cs b/src/AWS.Deploy.CLI/ServerMode/Exceptions.cs index ce8dbd105..7b8b6a38a 100644 --- a/src/AWS.Deploy.CLI/ServerMode/Exceptions.cs +++ b/src/AWS.Deploy.CLI/ServerMode/Exceptions.cs @@ -9,7 +9,6 @@ namespace AWS.Deploy.CLI.ServerMode /// /// Throw if the selected recommendation is null. /// - [AWSDeploymentExpectedException] public class SelectedRecommendationIsNullException : Exception { public SelectedRecommendationIsNullException(string message, Exception? innerException = null) : base(message, innerException) { } @@ -18,7 +17,6 @@ public SelectedRecommendationIsNullException(string message, Exception? innerExc /// /// Throw if the tool was not able to retrieve the AWS Credentials. /// - [AWSDeploymentExpectedException] public class FailedToRetrieveAWSCredentialsException : Exception { public FailedToRetrieveAWSCredentialsException(string message, Exception? innerException = null) : base(message, innerException) { } @@ -27,7 +25,6 @@ public FailedToRetrieveAWSCredentialsException(string message, Exception? innerE /// /// Throw if encryption key info passed in through stdin is invalid. /// - [AWSDeploymentExpectedException] public class InvalidEncryptionKeyInfoException : Exception { public InvalidEncryptionKeyInfoException(string message, Exception? innerException = null) : base(message, innerException) { } diff --git a/src/AWS.Deploy.CLI/ServerMode/Models/DeployToolExceptionSummary.cs b/src/AWS.Deploy.CLI/ServerMode/Models/DeployToolExceptionSummary.cs new file mode 100644 index 000000000..93e641ca2 --- /dev/null +++ b/src/AWS.Deploy.CLI/ServerMode/Models/DeployToolExceptionSummary.cs @@ -0,0 +1,17 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +namespace AWS.Deploy.CLI.ServerMode.Models +{ + public class DeployToolExceptionSummary + { + public string ErrorCode { get; set; } + public string Message { get; set; } + + public DeployToolExceptionSummary(string errorCode, string message) + { + ErrorCode = errorCode; + Message = message; + } + } +} diff --git a/src/AWS.Deploy.CLI/ServerMode/Models/GetDeploymentStatusOutput.cs b/src/AWS.Deploy.CLI/ServerMode/Models/GetDeploymentStatusOutput.cs index 9a7204464..22a8f41c1 100644 --- a/src/AWS.Deploy.CLI/ServerMode/Models/GetDeploymentStatusOutput.cs +++ b/src/AWS.Deploy.CLI/ServerMode/Models/GetDeploymentStatusOutput.cs @@ -11,5 +11,6 @@ namespace AWS.Deploy.CLI.ServerMode.Models public class GetDeploymentStatusOutput { public DeploymentStatus Status { get; set; } + public DeployToolExceptionSummary? Exception { get; set; } } } diff --git a/src/AWS.Deploy.CLI/ServerMode/Startup.cs b/src/AWS.Deploy.CLI/ServerMode/Startup.cs index 460308626..1b16a3f30 100644 --- a/src/AWS.Deploy.CLI/ServerMode/Startup.cs +++ b/src/AWS.Deploy.CLI/ServerMode/Startup.cs @@ -50,9 +50,10 @@ public void ConfigureServices(IServiceCollection services) { opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }); - + services.AddSwaggerGen(c => { + c.UseAllOfToExtendReferenceSchemas(); c.EnableAnnotations(); c.SwaggerDoc("v1", new OpenApiInfo { diff --git a/src/AWS.Deploy.CLI/ServerMode/Tasks/DeployRecommendationTask.cs b/src/AWS.Deploy.CLI/ServerMode/Tasks/DeployRecommendationTask.cs index 3df0db311..695be7844 100644 --- a/src/AWS.Deploy.CLI/ServerMode/Tasks/DeployRecommendationTask.cs +++ b/src/AWS.Deploy.CLI/ServerMode/Tasks/DeployRecommendationTask.cs @@ -36,13 +36,13 @@ private async Task CreateDeploymentBundle() { var dockerBuildDeploymentBundleResult = await _orchestrator.CreateContainerDeploymentBundle(_cloudApplication, _selectedRecommendation); if (!dockerBuildDeploymentBundleResult) - throw new FailedToCreateDeploymentBundleException("Failed to create a deployment bundle"); + throw new FailedToCreateDeploymentBundleException(DeployToolErrorCode.FailedToCreateContainerDeploymentBundle, "Failed to create a deployment bundle"); } else if (_selectedRecommendation.Recipe.DeploymentBundle == DeploymentBundleTypes.DotnetPublishZipFile) { var dotnetPublishDeploymentBundleResult = await _orchestrator.CreateDotnetPublishDeploymentBundle(_selectedRecommendation); if (!dotnetPublishDeploymentBundleResult) - throw new FailedToCreateDeploymentBundleException("Failed to create a deployment bundle"); + throw new FailedToCreateDeploymentBundleException(DeployToolErrorCode.FailedToCreateDotnetPublishDeploymentBundle, "Failed to create a deployment bundle"); } } } diff --git a/src/AWS.Deploy.CLI/Utilities/ProjectParserUtility.cs b/src/AWS.Deploy.CLI/Utilities/ProjectParserUtility.cs index 2030947f5..ec7f81b2d 100644 --- a/src/AWS.Deploy.CLI/Utilities/ProjectParserUtility.cs +++ b/src/AWS.Deploy.CLI/Utilities/ProjectParserUtility.cs @@ -50,7 +50,7 @@ public async Task Parse(string projectPath) else errorMessage = $"A project was not found at the path {projectPath}"; - throw new FailedToFindDeployableTargetException(errorMessage, ex); + throw new FailedToFindDeployableTargetException(DeployToolErrorCode.FailedToFindDeployableTarget, errorMessage, ex); } } } diff --git a/src/AWS.Deploy.Common/DeploymentManifest/DeploymentManifestEngine.cs b/src/AWS.Deploy.Common/DeploymentManifest/DeploymentManifestEngine.cs index d40284e80..313be6b4f 100644 --- a/src/AWS.Deploy.Common/DeploymentManifest/DeploymentManifestEngine.cs +++ b/src/AWS.Deploy.Common/DeploymentManifest/DeploymentManifestEngine.cs @@ -77,7 +77,7 @@ public async Task UpdateDeploymentManifestFile(string saveCdkDirectoryFullPath, } catch (Exception ex) { - throw new FailedToUpdateDeploymentManifestFileException($"Failed to update the deployment manifest file " + + throw new FailedToUpdateDeploymentManifestFileException(DeployToolErrorCode.DeploymentManifestUpdateFailed, $"Failed to update the deployment manifest file " + $"for the deployment project stored at '{saveCdkDirectoryFullPath}'", ex); } } diff --git a/src/AWS.Deploy.Common/Exceptions.cs b/src/AWS.Deploy.Common/Exceptions.cs index 606423df9..86efe10da 100644 --- a/src/AWS.Deploy.Common/Exceptions.cs +++ b/src/AWS.Deploy.Common/Exceptions.cs @@ -8,171 +8,223 @@ namespace AWS.Deploy.Common { - public class ProjectFileNotFoundException : Exception + public abstract class DeployToolException : Exception { - public ProjectFileNotFoundException(string projectPath) - : base($"A project was not found at the path {projectPath}.") + public DeployToolErrorCode ErrorCode { get; set; } + + public DeployToolException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(message, innerException) { - Path = projectPath; + ErrorCode = errorCode; } + } - public string Path { get; set; } + public enum DeployToolErrorCode + { + ProjectPathNotFound = 10000100, + ProjectParserNoSdkAttribute = 10000200, + InvalidCliArguments = 10000300, + SilentArgumentNeedsStackNameArgument = 10000400, + SilentArgumentNeedsDeploymentRecipe = 10000500, + DeploymentProjectPathNotFound = 10000600, + RuleHasInvalidTestType = 10000700, + MissingSystemCapabilities = 10000800, + NoDeploymentRecipesFound = 10000900, + NoCompatibleDeploymentRecipesFound = 10001000, + DeploymentProjectNotSupported = 10001100, + InvalidValueForOptionSettingItem = 10001200, + InvalidDockerBuildArgs = 10001300, + InvalidDockerExecutionDirectory = 10001400, + InvalidDotnetPublishArgs = 10001500, + ErrorParsingApplicationMetadata = 10001600, + FailedToCreateContainerDeploymentBundle = 10001700, + FailedToCreateDotnetPublishDeploymentBundle = 10001800, + OptionSettingItemDoesNotExistInRecipe = 10001900, + UnableToCreateValidatorInstance = 10002000, + OptionSettingItemValueValidationFailed = 10002100, + StackCreatedFromDifferentDeploymentRecommendation = 10002200, + DeploymentConfigurationNeedsAdjusting = 10002300, + UserDeploymentInvalidStackName = 10002400, + InvalidPropertyValueForUserDeployment = 10002500, + FailedToDeserializeUserDeploymentFile = 10002600, + DeploymentBundleDefinitionNotFound = 10002700, + DeploymentManifestUpdateFailed = 10002800, + DockerFileTemplateNotFound = 10002900, + UnableToMapProjectToDockerImage = 10003000, + NoValidDockerImageForProject = 10003100, + NoValidDockerMappingForSdkType = 10003200, + NoValidDockerMappingForTargetFramework = 10003300, + FailedToGenerateCDKProjectFromTemplate = 10003400, + FailedToInstallProjectTemplates = 10003500, + FailedToWritePackageJsonFile = 10003600, + FailedToInstallNpmPackages = 10003700, + DockerBuildFailed = 10003800, + FailedToGetCDKVersion = 10003900, + DockerLoginFailed = 10004000, + DockerTagFailed = 10004100, + DockerPushFailed = 10004200, + FailedToFindRecipeDefinitions = 10004300, + DotnetPublishFailed = 10004400, + FailedToFindZipUtility = 10004500, + ZipUtilityFailedToZip = 10004600, + FailedToGenerateDockerFile = 10004700, + BaseTemplatesInvalidPath = 10004800, + InvalidSolutionPath = 10004900, + InvalidAWSDeployRecipesCDKCommonVersion = 10005000, + FailedToDeployCdkApplication = 10005100, + AppRunnerServiceDoesNotExist = 10005200, + BeanstalkEnvironmentDoesNotExist = 10005300, + LoadBalancerDoesNotExist = 10005400, + LoadBalancerListenerDoesNotExist = 10005500, + CloudWatchRuleDoesNotExist = 10005600, + InvalidLocalUserSettingsFile = 10005700, + FailedToUpdateLocalUserSettingsFile = 10005800, + FailedToCheckDockerInfo = 10005900, + UnableToResolveAWSCredentials = 10006000, + UnableToCreateAWSCredentials = 10006100, + FailedToDeleteStack = 10006200, + FailedToFindDeployableTarget = 10006300, + BeanstalkAppPromptForNameReturnedNull = 10006400, + BeanstalkEnvPromptForNameReturnedNull = 10006500, + EC2KeyPairPromptForNameReturnedNull = 10006600, + TcpPortInUse = 10006700, + CompatibleRecommendationForRedeploymentNotFound = 10006800, + InvalidSaveDirectoryForCdkProject = 10006900, + FailedToFindDeploymentProjectRecipeId = 10007000, + UnexpectedError = 10007100 + } + + public class ProjectFileNotFoundException : DeployToolException + { + public ProjectFileNotFoundException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) + : base(errorCode, message, innerException) { } } /// /// Common exception if the user has passed invalid input from the command line /// - [AWSDeploymentExpectedException] - public class InvalidCliArgumentException : Exception + public class InvalidCliArgumentException : DeployToolException { - public InvalidCliArgumentException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidCliArgumentException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if the user attempts to deploy a but the recipe definition is invalid /// - [AWSDeploymentExpectedException] - public class InvalidRecipeDefinitionException : Exception + public class InvalidRecipeDefinitionException : DeployToolException { - public InvalidRecipeDefinitionException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidRecipeDefinitionException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if the user attempts to deploy a but the project definition is invalid /// - [AWSDeploymentExpectedException] - public class InvalidProjectDefinitionException : Exception + public class InvalidProjectDefinitionException : DeployToolException { - public InvalidProjectDefinitionException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidProjectDefinitionException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Thrown if there is a missing System Capability. /// - [AWSDeploymentExpectedException] - public class MissingSystemCapabilityException : Exception + public class MissingSystemCapabilityException : DeployToolException { - public MissingSystemCapabilityException(string message, Exception? innerException = null) : base(message, innerException) { } + public MissingSystemCapabilityException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if Recommendation Engine is unable to generate /// recommendations for a given target context /// - [AWSDeploymentExpectedException] - public class FailedToGenerateAnyRecommendations : Exception + public class FailedToGenerateAnyRecommendations : DeployToolException { - public FailedToGenerateAnyRecommendations(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToGenerateAnyRecommendations(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if a value is set that is not part of the allowed values /// of an option setting item /// - [AWSDeploymentExpectedException] - public class InvalidOverrideValueException : Exception + public class InvalidOverrideValueException : DeployToolException { - public InvalidOverrideValueException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidOverrideValueException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if there is a parse error reading the existing Cloud Application's metadata /// - [AWSDeploymentExpectedException] - public class ParsingExistingCloudApplicationMetadataException : Exception + public class ParsingExistingCloudApplicationMetadataException : DeployToolException { - public ParsingExistingCloudApplicationMetadataException(string message, Exception? innerException = null) : base(message, innerException) { } + public ParsingExistingCloudApplicationMetadataException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if Orchestrator is unable to create /// the deployment bundle. /// - [AWSDeploymentExpectedException] - public class FailedToCreateDeploymentBundleException : Exception + public class FailedToCreateDeploymentBundleException : DeployToolException { - public FailedToCreateDeploymentBundleException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToCreateDeploymentBundleException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if Option Setting Item does not exist /// - [AWSDeploymentExpectedException] - public class OptionSettingItemDoesNotExistException : Exception + public class OptionSettingItemDoesNotExistException : DeployToolException { - public OptionSettingItemDoesNotExistException(string message, Exception? innerException = null) : base(message, innerException) { } + public OptionSettingItemDoesNotExistException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } - [AWSDeploymentExpectedException] - public class InvalidValidatorTypeException : Exception + public class InvalidValidatorTypeException : DeployToolException { - public InvalidValidatorTypeException(string? message, Exception? innerException = null) : base(message, innerException) { } + public InvalidValidatorTypeException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Thrown if is given an invalid value. /// - [AWSDeploymentExpectedException] - public class ValidationFailedException : Exception + public class ValidationFailedException : DeployToolException { - public ValidationFailedException(string? message, Exception? innerException = null) : base(message, innerException) { } + public ValidationFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception thrown if Project Path contains an invalid path /// - [AWSDeploymentExpectedException] - public class InvalidProjectPathException : Exception + public class InvalidProjectPathException : DeployToolException { - public InvalidProjectPathException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidProjectPathException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// Throw if an invalid is used. /// - [AWSDeploymentExpectedException] - public class InvalidUserDeploymentSettingsException : Exception + public class InvalidUserDeploymentSettingsException : DeployToolException { - public InvalidUserDeploymentSettingsException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidUserDeploymentSettingsException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if we cannot retrieve deployment bundle definitions /// - [AWSDeploymentExpectedException] - public class NoDeploymentBundleDefinitionsFoundException : Exception + public class NoDeploymentBundleDefinitionsFoundException : DeployToolException { - public NoDeploymentBundleDefinitionsFoundException(string message, Exception? innerException = null) : base(message, innerException) { } + public NoDeploymentBundleDefinitionsFoundException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// Exception thrown if a failure occured while trying to update the deployment manifest file. /// - [AWSDeploymentExpectedException] - public class FailedToUpdateDeploymentManifestFileException : Exception + public class FailedToUpdateDeploymentManifestFileException : DeployToolException { - public FailedToUpdateDeploymentManifestFileException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToUpdateDeploymentManifestFileException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } - /// - /// Indicates a specific strongly typed Exception can be anticipated. - /// Whoever throws this error should also present the user with helpful information - /// on what wrong and how to fix it. This is the preferred UX. - /// - /// Conversely, if an Exceptions not marked with attribute reaches the entry point - /// application (ie Program.cs) than all exception details will likely be presented to - /// user. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] - public class AWSDeploymentExpectedExceptionAttribute : Attribute { } - public static class ExceptionExtensions { /// - /// True if the is decorated with - /// . + /// True if the inherits from + /// . /// public static bool IsAWSDeploymentExpectedException(this Exception e) => - null != e?.GetType() - .GetCustomAttribute(typeof(AWSDeploymentExpectedExceptionAttribute), inherit: true); + e is DeployToolException; public static string PrettyPrint(this Exception? e) { diff --git a/src/AWS.Deploy.Common/ProjectDefinition.cs b/src/AWS.Deploy.Common/ProjectDefinition.cs index b7282023d..85ea1edf4 100644 --- a/src/AWS.Deploy.Common/ProjectDefinition.cs +++ b/src/AWS.Deploy.Common/ProjectDefinition.cs @@ -85,7 +85,7 @@ public ProjectDefinition( private bool CheckIfDockerFileExists(string projectPath) { var dir = Directory.GetFiles(new FileInfo(projectPath).DirectoryName ?? - throw new InvalidProjectPathException("The project path is invalid."), "Dockerfile"); + throw new InvalidProjectPathException(DeployToolErrorCode.ProjectPathNotFound, "The project path is invalid."), "Dockerfile"); return dir.Length == 1; } diff --git a/src/AWS.Deploy.Common/ProjectDefinitionParser.cs b/src/AWS.Deploy.Common/ProjectDefinitionParser.cs index 31885fd80..ffe5b6043 100644 --- a/src/AWS.Deploy.Common/ProjectDefinitionParser.cs +++ b/src/AWS.Deploy.Common/ProjectDefinitionParser.cs @@ -62,7 +62,7 @@ public async Task Parse(string projectPath) if (!_fileManager.Exists(projectPath)) { - throw new ProjectFileNotFoundException(projectPath); + throw new ProjectFileNotFoundException(DeployToolErrorCode.ProjectPathNotFound, $"A project was not found at the path {projectPath}."); } var xmlProjectFile = new XmlDocument(); @@ -73,7 +73,7 @@ public async Task Parse(string projectPath) projectPath, await GetProjectSolutionFile(projectPath), xmlProjectFile.DocumentElement?.Attributes["Sdk"]?.Value ?? - throw new InvalidProjectDefinitionException( + throw new InvalidProjectDefinitionException(DeployToolErrorCode.ProjectParserNoSdkAttribute, "The project file that is being referenced does not contain and 'Sdk' attribute.") ); diff --git a/src/AWS.Deploy.Common/Recipes/OptionSettingItem.ValueOverride.cs b/src/AWS.Deploy.Common/Recipes/OptionSettingItem.ValueOverride.cs index bbfa78a9a..1196cb3ce 100644 --- a/src/AWS.Deploy.Common/Recipes/OptionSettingItem.ValueOverride.cs +++ b/src/AWS.Deploy.Common/Recipes/OptionSettingItem.ValueOverride.cs @@ -108,11 +108,11 @@ public void SetValueOverride(object valueOverride) } } if (!isValid) - throw new ValidationFailedException(validationFailedMessage.Trim()); + throw new ValidationFailedException(DeployToolErrorCode.OptionSettingItemValueValidationFailed, validationFailedMessage.Trim()); if (AllowedValues != null && AllowedValues.Count > 0 && valueOverride != null && !AllowedValues.Contains(valueOverride.ToString() ?? "")) - throw new InvalidOverrideValueException($"Invalid value for option setting item '{Name}'"); + throw new InvalidOverrideValueException(DeployToolErrorCode.InvalidValueForOptionSettingItem, $"Invalid value for option setting item '{Name}'"); if (valueOverride is bool || valueOverride is int || valueOverride is long || valueOverride is double) { diff --git a/src/AWS.Deploy.Common/Recipes/Validation/ValidatorFactory.cs b/src/AWS.Deploy.Common/Recipes/Validation/ValidatorFactory.cs index 282675fe2..f25c5ae93 100644 --- a/src/AWS.Deploy.Common/Recipes/Validation/ValidatorFactory.cs +++ b/src/AWS.Deploy.Common/Recipes/Validation/ValidatorFactory.cs @@ -47,7 +47,7 @@ public static IRecipeValidator[] BuildValidators(this RecipeDefinition recipeDef { var validatorInstance = Activator.CreateInstance(typeMappings[validatorType]); if (validatorInstance == null) - throw new InvalidValidatorTypeException($"Could not create an instance of validator type {validatorType}"); + throw new InvalidValidatorTypeException(DeployToolErrorCode.UnableToCreateValidatorInstance, $"Could not create an instance of validator type {validatorType}"); return validatorInstance; } @@ -55,7 +55,7 @@ public static IRecipeValidator[] BuildValidators(this RecipeDefinition recipeDef { var validatorInstance = jObject.ToObject(typeMappings[validatorType]); if (validatorInstance == null) - throw new InvalidValidatorTypeException($"Could not create an instance of validator type {validatorType}"); + throw new InvalidValidatorTypeException(DeployToolErrorCode.UnableToCreateValidatorInstance, $"Could not create an instance of validator type {validatorType}"); return validatorInstance; } diff --git a/src/AWS.Deploy.Common/Recommendation.cs b/src/AWS.Deploy.Common/Recommendation.cs index d23caf2d1..c30c2f5a2 100644 --- a/src/AWS.Deploy.Common/Recommendation.cs +++ b/src/AWS.Deploy.Common/Recommendation.cs @@ -98,7 +98,7 @@ public IEnumerable GetConfigurableOptionSettingItems() public OptionSettingItem GetOptionSetting(string? jsonPath) { if (string.IsNullOrEmpty(jsonPath)) - throw new OptionSettingItemDoesNotExistException($"The Option Setting Item {jsonPath} does not exist as part of the" + + throw new OptionSettingItemDoesNotExistException(DeployToolErrorCode.OptionSettingItemDoesNotExistInRecipe, $"The Option Setting Item {jsonPath} does not exist as part of the" + $" {Recipe.Name} recipe"); var ids = jsonPath.Split('.'); @@ -110,7 +110,7 @@ public OptionSettingItem GetOptionSetting(string? jsonPath) optionSetting = optionSettings.FirstOrDefault(os => os.Id.Equals(id)); if (optionSetting == null) { - throw new OptionSettingItemDoesNotExistException($"The Option Setting Item {jsonPath} does not exist as part of the" + + throw new OptionSettingItemDoesNotExistException(DeployToolErrorCode.OptionSettingItemDoesNotExistInRecipe, $"The Option Setting Item {jsonPath} does not exist as part of the" + $" {Recipe.Name} recipe"); } } diff --git a/src/AWS.Deploy.Common/UserDeploymentSettings.cs b/src/AWS.Deploy.Common/UserDeploymentSettings.cs index ccaaa0620..6b94aa81c 100644 --- a/src/AWS.Deploy.Common/UserDeploymentSettings.cs +++ b/src/AWS.Deploy.Common/UserDeploymentSettings.cs @@ -42,7 +42,7 @@ public class UserDeploymentSettings } catch (Exception ex) { - throw new InvalidUserDeploymentSettingsException("An error occured while trying to deserialize the User Deployment Settings file.", ex); + throw new InvalidUserDeploymentSettingsException(DeployToolErrorCode.FailedToDeserializeUserDeploymentFile, "An error occured while trying to deserialize the User Deployment Settings file.", ex); } } diff --git a/src/AWS.Deploy.DockerEngine/DockerEngine.cs b/src/AWS.Deploy.DockerEngine/DockerEngine.cs index 140478a28..aa12ba2ec 100644 --- a/src/AWS.Deploy.DockerEngine/DockerEngine.cs +++ b/src/AWS.Deploy.DockerEngine/DockerEngine.cs @@ -56,7 +56,7 @@ public void GenerateDockerFile() var imageMapping = GetImageMapping(); if (imageMapping == null) { - throw new UnknownDockerImageException($"Unable to determine a valid docker base and build image for project of type {_project.SdkType} and Target Framework {_project.TargetFramework}"); + throw new UnknownDockerImageException(DeployToolErrorCode.NoValidDockerImageForProject, $"Unable to determine a valid docker base and build image for project of type {_project.SdkType} and Target Framework {_project.TargetFramework}"); } var dockerFile = new DockerFile(imageMapping, projectFileName, _project.AssemblyName); @@ -131,10 +131,10 @@ private ImageMapping GetImageMapping() var definitions = JsonConvert.DeserializeObject>(content); var mappings = definitions.FirstOrDefault(x => x.SdkType.Equals(_project.SdkType)); if (mappings == null) - throw new UnsupportedProjectException($"The project with SDK Type {_project.SdkType} is not supported."); + throw new UnsupportedProjectException(DeployToolErrorCode.NoValidDockerMappingForSdkType, $"The project with SDK Type {_project.SdkType} is not supported."); return mappings.ImageMapping.FirstOrDefault(x => x.TargetFramework.Equals(_project.TargetFramework)) - ?? throw new UnsupportedProjectException($"The project with Target Framework {_project.TargetFramework} is not supported."); + ?? throw new UnsupportedProjectException(DeployToolErrorCode.NoValidDockerMappingForTargetFramework, $"The project with Target Framework {_project.TargetFramework} is not supported."); } /// diff --git a/src/AWS.Deploy.DockerEngine/Exceptions.cs b/src/AWS.Deploy.DockerEngine/Exceptions.cs index d844c066a..52de5da32 100644 --- a/src/AWS.Deploy.DockerEngine/Exceptions.cs +++ b/src/AWS.Deploy.DockerEngine/Exceptions.cs @@ -2,31 +2,32 @@ // SPDX-License-Identifier: Apache-2.0 using System; +using AWS.Deploy.Common; namespace AWS.Deploy.DockerEngine { public class DockerFileTemplateException : DockerEngineExceptionBase { - public DockerFileTemplateException(string message) : base(message) { } + public DockerFileTemplateException(DeployToolErrorCode errorCode, string message) : base(errorCode, message) { } } public class DockerEngineException : DockerEngineExceptionBase { - public DockerEngineException(string message) : base(message) { } + public DockerEngineException(DeployToolErrorCode errorCode, string message) : base(errorCode, message) { } } public class UnknownDockerImageException : DockerEngineExceptionBase { - public UnknownDockerImageException(string message) : base(message) { } + public UnknownDockerImageException(DeployToolErrorCode errorCode, string message) : base(errorCode, message) { } } - public class DockerEngineExceptionBase : Exception + public class DockerEngineExceptionBase : DeployToolException { - public DockerEngineExceptionBase(string message) : base(message) { } + public DockerEngineExceptionBase(DeployToolErrorCode errorCode, string message) : base(errorCode, message) { } } public class UnsupportedProjectException : DockerEngineExceptionBase { - public UnsupportedProjectException(string message) : base(message) { } + public UnsupportedProjectException(DeployToolErrorCode errorCode, string message) : base(errorCode, message) { } } } diff --git a/src/AWS.Deploy.DockerEngine/ProjectUtilities.cs b/src/AWS.Deploy.DockerEngine/ProjectUtilities.cs index 70ba5a01b..79946b4f6 100644 --- a/src/AWS.Deploy.DockerEngine/ProjectUtilities.cs +++ b/src/AWS.Deploy.DockerEngine/ProjectUtilities.cs @@ -3,6 +3,7 @@ using System.IO; using System.Reflection; +using AWS.Deploy.Common; using AWS.Deploy.Common.Extensions; namespace AWS.Deploy.DockerEngine @@ -21,7 +22,7 @@ internal static string ReadDockerFileConfig() if (string.IsNullOrWhiteSpace(template)) { - throw new DockerEngineException($"The DockerEngine could not find the embedded config file responsible for mapping projects to Docker images."); + throw new DockerEngineException(DeployToolErrorCode.UnableToMapProjectToDockerImage, $"The DockerEngine could not find the embedded config file responsible for mapping projects to Docker images."); } return template; @@ -36,7 +37,7 @@ internal static string ReadTemplate() if (string.IsNullOrWhiteSpace(template)) { - throw new DockerFileTemplateException("The Dockerfile template for the project was not found."); + throw new DockerFileTemplateException(DeployToolErrorCode.DockerFileTemplateNotFound, "The Dockerfile template for the project was not found."); } return template; diff --git a/src/AWS.Deploy.Orchestration/CDK/CDKInstaller.cs b/src/AWS.Deploy.Orchestration/CDK/CDKInstaller.cs index d6e59f159..65e9985ba 100644 --- a/src/AWS.Deploy.Orchestration/CDK/CDKInstaller.cs +++ b/src/AWS.Deploy.Orchestration/CDK/CDKInstaller.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using AWS.Deploy.Common; using AWS.Deploy.Orchestration.Utilities; namespace AWS.Deploy.Orchestration.CDK @@ -52,7 +53,7 @@ public async Task> GetVersion(string workingDirectory) } catch (Exception exception) { - throw new NPMCommandFailedException($"Failed to execute {command}", exception); + throw new NPMCommandFailedException(DeployToolErrorCode.FailedToGetCDKVersion, $"Failed to execute {command}", exception); } var standardOut = result.StandardOut ?? ""; diff --git a/src/AWS.Deploy.Orchestration/CDK/NPMPackageInitializer.cs b/src/AWS.Deploy.Orchestration/CDK/NPMPackageInitializer.cs index c5759a294..1ced1fd2a 100644 --- a/src/AWS.Deploy.Orchestration/CDK/NPMPackageInitializer.cs +++ b/src/AWS.Deploy.Orchestration/CDK/NPMPackageInitializer.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Threading.Tasks; +using AWS.Deploy.Common; using AWS.Deploy.Common.IO; using AWS.Deploy.Orchestration.Utilities; @@ -85,7 +86,7 @@ public async Task Initialize(string workingDirectory, Version cdkVersion) } catch (Exception exception) { - throw new PackageJsonFileException($"Failed to write {_packageJsonFileName} at {packageJsonFilePath}", exception); + throw new PackageJsonFileException(DeployToolErrorCode.FailedToWritePackageJsonFile, $"Failed to write {_packageJsonFileName} at {packageJsonFilePath}", exception); } try @@ -95,7 +96,7 @@ public async Task Initialize(string workingDirectory, Version cdkVersion) } catch (Exception exception) { - throw new NPMCommandFailedException($"Failed to install npm packages at {workingDirectory}", exception); + throw new NPMCommandFailedException(DeployToolErrorCode.FailedToInstallNpmPackages, $"Failed to install npm packages at {workingDirectory}", exception); } } } diff --git a/src/AWS.Deploy.Orchestration/CdkAppSettingsSerializer.cs b/src/AWS.Deploy.Orchestration/CdkAppSettingsSerializer.cs index 48e1d0d48..f9e55467b 100644 --- a/src/AWS.Deploy.Orchestration/CdkAppSettingsSerializer.cs +++ b/src/AWS.Deploy.Orchestration/CdkAppSettingsSerializer.cs @@ -15,7 +15,7 @@ public string Build(CloudApplication cloudApplication, Recommendation recommenda { var projectPath = new FileInfo(recommendation.ProjectPath).Directory?.FullName; if (string.IsNullOrEmpty(projectPath)) - throw new InvalidProjectPathException("The project path provided is invalid."); + throw new InvalidProjectPathException(DeployToolErrorCode.ProjectPathNotFound, "The project path provided is invalid."); // General Settings var appSettingsContainer = new RecipeProps>( diff --git a/src/AWS.Deploy.Orchestration/CdkProjectHandler.cs b/src/AWS.Deploy.Orchestration/CdkProjectHandler.cs index b8a7f680c..6ab89ac1a 100644 --- a/src/AWS.Deploy.Orchestration/CdkProjectHandler.cs +++ b/src/AWS.Deploy.Orchestration/CdkProjectHandler.cs @@ -87,7 +87,7 @@ await _commandLineWrapper.Run($"npx cdk bootstrap aws://{session.AWSAccountId}/{ streamOutputToInteractiveService: true); if (cdkDeploy.ExitCode != 0) - throw new FailedToDeployCDKAppException("We had an issue deploying your application to AWS. Check the deployment output for more details."); + throw new FailedToDeployCDKAppException(DeployToolErrorCode.FailedToDeployCdkApplication, "We had an issue deploying your application to AWS. Check the deployment output for more details."); } public string CreateCdkProject(Recommendation recommendation, OrchestratorSession session, string? saveCdkDirectoryPath = null) diff --git a/src/AWS.Deploy.Orchestration/Data/AWSResourceQueryer.cs b/src/AWS.Deploy.Orchestration/Data/AWSResourceQueryer.cs index 79d25f262..df5f30ab4 100644 --- a/src/AWS.Deploy.Orchestration/Data/AWSResourceQueryer.cs +++ b/src/AWS.Deploy.Orchestration/Data/AWSResourceQueryer.cs @@ -89,7 +89,7 @@ public AWSResourceQueryer(IAWSClientFactory awsClientFactory) if (service == null) { - throw new AWSResourceNotFoundException($"The AppRunner service '{serviceArn}' does not exist."); + throw new AWSResourceNotFoundException(DeployToolErrorCode.AppRunnerServiceDoesNotExist, $"The AppRunner service '{serviceArn}' does not exist."); } return service; @@ -113,7 +113,7 @@ public async Task DescribeElasticBeanstalkEnvironment(st if (!environment.Environments.Any()) { - throw new AWSResourceNotFoundException($"The elastic beanstalk environment '{environmentId}' does not exist."); + throw new AWSResourceNotFoundException(DeployToolErrorCode.BeanstalkEnvironmentDoesNotExist, $"The elastic beanstalk environment '{environmentId}' does not exist."); } return environment.Environments.First(); @@ -130,7 +130,7 @@ public async Task DescribeElasticBeanstalkEnvironment(st if (!loadBalancers.LoadBalancers.Any()) { - throw new AWSResourceNotFoundException($"The load balancer '{loadBalancerArn}' does not exist."); + throw new AWSResourceNotFoundException(DeployToolErrorCode.LoadBalancerDoesNotExist, $"The load balancer '{loadBalancerArn}' does not exist."); } return loadBalancers.LoadBalancers.First(); @@ -147,7 +147,7 @@ public async Task DescribeElasticBeanstalkEnvironment(st if (!listeners.Listeners.Any()) { - throw new AWSResourceNotFoundException($"The load balancer '{loadBalancerArn}' does not have any listeners."); + throw new AWSResourceNotFoundException(DeployToolErrorCode.LoadBalancerListenerDoesNotExist, $"The load balancer '{loadBalancerArn}' does not have any listeners."); } return listeners.Listeners; @@ -164,7 +164,7 @@ public async Task DescribeCloudWatchRule(string ruleName) if (rule == null) { - throw new AWSResourceNotFoundException($"The CloudWatch rule'{ruleName}' does not exist."); + throw new AWSResourceNotFoundException(DeployToolErrorCode.CloudWatchRuleDoesNotExist, $"The CloudWatch rule'{ruleName}' does not exist."); } return rule; diff --git a/src/AWS.Deploy.Orchestration/DeploymentBundleHandler.cs b/src/AWS.Deploy.Orchestration/DeploymentBundleHandler.cs index de1dc8324..18e3f88b0 100644 --- a/src/AWS.Deploy.Orchestration/DeploymentBundleHandler.cs +++ b/src/AWS.Deploy.Orchestration/DeploymentBundleHandler.cs @@ -65,7 +65,7 @@ public async Task BuildDockerImage(CloudApplication cloudApplication, Re var result = await _commandLineWrapper.TryRunWithResult(dockerBuildCommand, dockerExecutionDirectory, streamOutputToInteractiveService: true); if (result.ExitCode != 0) { - throw new DockerBuildFailedException(result.StandardError ?? ""); + throw new DockerBuildFailedException(DeployToolErrorCode.DockerBuildFailed, result.StandardError ?? ""); } return imageTag; @@ -120,7 +120,7 @@ public async Task CreateDotnetPublishZip(Recommendation recommendation) var result = await _commandLineWrapper.TryRunWithResult(publishCommand, streamOutputToInteractiveService: true); if (result.ExitCode != 0) { - throw new DotnetPublishFailedException(result.StandardError ?? ""); + throw new DotnetPublishFailedException(DeployToolErrorCode.DotnetPublishFailed, result.StandardError ?? ""); } var zipFilePath = $"{publishDirectoryInfo.FullName}.zip"; @@ -143,7 +143,7 @@ private string GetDockerExecutionDirectory(Recommendation recommendation) var dockerExecutionDirectory = recommendation.DeploymentBundle.DockerExecutionDirectory; var dockerFileDirectory = new FileInfo(recommendation.ProjectPath).Directory?.FullName; if (dockerFileDirectory == null) - throw new InvalidProjectPathException("The project path is invalid."); + throw new InvalidProjectPathException(DeployToolErrorCode.ProjectPathNotFound, "The project path is invalid."); var projectSolutionPath = recommendation.ProjectDefinition.ProjectSolutionPath; if (string.IsNullOrEmpty(dockerExecutionDirectory)) @@ -155,7 +155,7 @@ private string GetDockerExecutionDirectory(Recommendation recommendation) else { var projectSolutionDirectory = new FileInfo(projectSolutionPath).Directory?.FullName; - dockerExecutionDirectory = projectSolutionDirectory ?? throw new InvalidSolutionPathException("The solution path is invalid."); + dockerExecutionDirectory = projectSolutionDirectory ?? throw new InvalidSolutionPathException(DeployToolErrorCode.InvalidSolutionPath, "The solution path is invalid."); } } @@ -166,7 +166,7 @@ private string GetDockerFilePath(Recommendation recommendation) { var dockerFileDirectory = new FileInfo(recommendation.ProjectPath).Directory?.FullName; if (dockerFileDirectory == null) - throw new InvalidProjectPathException("The project path is invalid."); + throw new InvalidProjectPathException(DeployToolErrorCode.ProjectPathNotFound, "The project path is invalid."); return Path.Combine(dockerFileDirectory, "Dockerfile"); } @@ -195,7 +195,7 @@ private async Task InitiateDockerLogin() var authorizationTokens = await _awsResourceQueryer.GetECRAuthorizationToken(); if (authorizationTokens.Count == 0) - throw new DockerLoginFailedException("Failed to login to Docker"); + throw new DockerLoginFailedException(DeployToolErrorCode.DockerLoginFailed, "Failed to login to Docker"); var authTokenBytes = Convert.FromBase64String(authorizationTokens[0].AuthorizationToken); var authToken = Encoding.UTF8.GetString(authTokenBytes); @@ -205,7 +205,7 @@ private async Task InitiateDockerLogin() var result = await _commandLineWrapper.TryRunWithResult(dockerLoginCommand, streamOutputToInteractiveService: true); if (result.ExitCode != 0) - throw new DockerLoginFailedException("Failed to login to Docker"); + throw new DockerLoginFailedException(DeployToolErrorCode.DockerLoginFailed, "Failed to login to Docker"); } private async Task SetupECRRepository(string ecrRepositoryName) @@ -228,7 +228,7 @@ private async Task TagDockerImage(string sourceTagName, string targetTagName) var result = await _commandLineWrapper.TryRunWithResult(dockerTagCommand, streamOutputToInteractiveService: true); if (result.ExitCode != 0) - throw new DockerTagFailedException("Failed to tag Docker image"); + throw new DockerTagFailedException(DeployToolErrorCode.DockerTagFailed, "Failed to tag Docker image"); } private async Task PushDockerImage(string targetTagName) @@ -237,7 +237,7 @@ private async Task PushDockerImage(string targetTagName) var result = await _commandLineWrapper.TryRunWithResult(dockerPushCommand, streamOutputToInteractiveService: true); if (result.ExitCode != 0) - throw new DockerPushFailedException("Failed to push Docker Image"); + throw new DockerPushFailedException(DeployToolErrorCode.DockerPushFailed, "Failed to push Docker Image"); } } } diff --git a/src/AWS.Deploy.Orchestration/Exceptions.cs b/src/AWS.Deploy.Orchestration/Exceptions.cs index 345990dab..b4acc3822 100644 --- a/src/AWS.Deploy.Orchestration/Exceptions.cs +++ b/src/AWS.Deploy.Orchestration/Exceptions.cs @@ -7,189 +7,168 @@ namespace AWS.Deploy.Orchestration /// /// Exception is thrown if Microsoft Templating Engine is unable to generate a template /// - [AWSDeploymentExpectedException] - public class TemplateGenerationFailedException : Exception + public class TemplateGenerationFailedException : DeployToolException { - public TemplateGenerationFailedException(string message, Exception? innerException = null) : base(message, innerException) { } + public TemplateGenerationFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if Microsoft Templating Engine is unable to find location to install templates from /// - [AWSDeploymentExpectedException] - public class DefaultTemplateInstallationFailedException : Exception + public class DefaultTemplateInstallationFailedException : DeployToolException { - public DefaultTemplateInstallationFailedException(string message, Exception? innerException = null) : base(message, innerException) { } + public DefaultTemplateInstallationFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if Microsoft Templating Engine returns an error when running a command /// - [AWSDeploymentExpectedException] - public class RunCommandFailedException : Exception + public class RunCommandFailedException : DeployToolException { - public RunCommandFailedException(string message, Exception? innerException = null) : base(message, innerException) { } + public RunCommandFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if package.json file IO fails. /// - [AWSDeploymentExpectedException] - public class PackageJsonFileException : Exception + public class PackageJsonFileException : DeployToolException { - public PackageJsonFileException(string message, Exception? innerException = null) : base(message, innerException) { } + public PackageJsonFileException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if docker build attempt failed /// - [AWSDeploymentExpectedException] - public class DockerBuildFailedException : Exception + public class DockerBuildFailedException : DeployToolException { - public DockerBuildFailedException(string message, Exception? innerException = null) : base(message, innerException) { } + public DockerBuildFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if npm command fails to execute. /// - [AWSDeploymentExpectedException] - public class NPMCommandFailedException : Exception + public class NPMCommandFailedException : DeployToolException { - public NPMCommandFailedException(string message, Exception? innerException = null) : base(message, innerException) { } + public NPMCommandFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if docker login attempt failed /// - [AWSDeploymentExpectedException] - public class DockerLoginFailedException : Exception + public class DockerLoginFailedException : DeployToolException { - public DockerLoginFailedException(string message, Exception? innerException = null) : base(message, innerException) { } + public DockerLoginFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if docker tag attempt failed /// - [AWSDeploymentExpectedException] - public class DockerTagFailedException : Exception + public class DockerTagFailedException : DeployToolException { - public DockerTagFailedException(string message, Exception? innerException = null) : base(message, innerException) { } + public DockerTagFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if docker push attempt failed /// - [AWSDeploymentExpectedException] - public class DockerPushFailedException : Exception + public class DockerPushFailedException : DeployToolException { - public DockerPushFailedException(string message, Exception? innerException = null) : base(message, innerException) { } + public DockerPushFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if we cannot retrieve recipe definitions /// - [AWSDeploymentExpectedException] - public class NoRecipeDefinitionsFoundException : Exception + public class NoRecipeDefinitionsFoundException : DeployToolException { - public NoRecipeDefinitionsFoundException(string message, Exception? innerException = null) : base(message, innerException) { } + public NoRecipeDefinitionsFoundException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception is thrown if dotnet publish attempt failed /// - [AWSDeploymentExpectedException] - public class DotnetPublishFailedException : Exception + public class DotnetPublishFailedException : DeployToolException { - public DotnetPublishFailedException(string message, Exception? innerException = null) : base(message, innerException) { } + public DotnetPublishFailedException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if Zip File Manager fails to create a Zip File /// - [AWSDeploymentExpectedException] - public class FailedToCreateZipFileException : Exception + public class FailedToCreateZipFileException : DeployToolException { - public FailedToCreateZipFileException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToCreateZipFileException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception thrown if Docker file could not be generated /// - [AWSDeploymentExpectedException] - public class FailedToGenerateDockerFileException : Exception + public class FailedToGenerateDockerFileException : DeployToolException { - public FailedToGenerateDockerFileException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToGenerateDockerFileException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception thrown if RecipePath contains an invalid path /// - [AWSDeploymentExpectedException] - public class InvalidRecipePathException : Exception + public class InvalidRecipePathException : DeployToolException { - public InvalidRecipePathException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidRecipePathException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception thrown if Solution Path contains an invalid path /// - [AWSDeploymentExpectedException] - public class InvalidSolutionPathException : Exception + public class InvalidSolutionPathException : DeployToolException { - public InvalidSolutionPathException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidSolutionPathException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception thrown if AWS Deploy Recipes CDK Common Product Version is invalid. /// - [AWSDeploymentExpectedException] - public class InvalidAWSDeployRecipesCDKCommonVersionException : Exception + public class InvalidAWSDeployRecipesCDKCommonVersionException : DeployToolException { - public InvalidAWSDeployRecipesCDKCommonVersionException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidAWSDeployRecipesCDKCommonVersionException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception thrown if the 'cdk deploy' command failed. /// - [AWSDeploymentExpectedException] - public class FailedToDeployCDKAppException : Exception + public class FailedToDeployCDKAppException : DeployToolException { - public FailedToDeployCDKAppException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToDeployCDKAppException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception thrown if an AWS Resource is not found or does not exist. /// - [AWSDeploymentExpectedException] - public class AWSResourceNotFoundException : Exception + public class AWSResourceNotFoundException : DeployToolException { - public AWSResourceNotFoundException(string message, Exception? innerException = null) : base(message, innerException) { } + public AWSResourceNotFoundException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception thrown if the Local User Settings File is invalid. /// - [AWSDeploymentExpectedException] - public class InvalidLocalUserSettingsFileException : Exception + public class InvalidLocalUserSettingsFileException : DeployToolException { - public InvalidLocalUserSettingsFileException(string message, Exception? innerException = null) : base(message, innerException) { } + public InvalidLocalUserSettingsFileException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Exception thrown if a failure occured while trying to update the Local User Settings file. /// - [AWSDeploymentExpectedException] - public class FailedToUpdateLocalUserSettingsFileException : Exception + public class FailedToUpdateLocalUserSettingsFileException : DeployToolException { - public FailedToUpdateLocalUserSettingsFileException(string message, Exception? innerException = null) : base(message, innerException) { } + public FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } /// /// Throw if docker info failed to return output. /// - [AWSDeploymentExpectedException] - public class DockerInfoException : Exception + public class DockerInfoException : DeployToolException { - public DockerInfoException(string message, Exception? innerException = null) : base(message, innerException) { } + public DockerInfoException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } } diff --git a/src/AWS.Deploy.Orchestration/LocalUserSettings/LocalUserSettingsEngine.cs b/src/AWS.Deploy.Orchestration/LocalUserSettings/LocalUserSettingsEngine.cs index 8203333cb..502aa578d 100644 --- a/src/AWS.Deploy.Orchestration/LocalUserSettings/LocalUserSettingsEngine.cs +++ b/src/AWS.Deploy.Orchestration/LocalUserSettings/LocalUserSettingsEngine.cs @@ -42,9 +42,9 @@ public async Task UpdateLastDeployedStack(string stackName, string projectName, try { if (string.IsNullOrEmpty(projectName)) - throw new FailedToUpdateLocalUserSettingsFileException("The Project Name is not defined."); + throw new FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode.FailedToUpdateLocalUserSettingsFile, "The Project Name is not defined."); if (string.IsNullOrEmpty(awsAccountId) || string.IsNullOrEmpty(awsRegion)) - throw new FailedToUpdateLocalUserSettingsFileException("The AWS Account Id or Region is not defined."); + throw new FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode.FailedToUpdateLocalUserSettingsFile, "The AWS Account Id or Region is not defined."); var localUserSettings = await GetLocalUserSettings(); var lastDeployedStack = localUserSettings?.LastDeployedStacks? @@ -105,7 +105,7 @@ public async Task UpdateLastDeployedStack(string stackName, string projectName, } catch (Exception ex) { - throw new FailedToUpdateLocalUserSettingsFileException($"Failed to update the local user settings file " + + throw new FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode.FailedToUpdateLocalUserSettingsFile, $"Failed to update the local user settings file " + $"to include the last deployed to stack '{stackName}'.", ex); } } @@ -118,9 +118,9 @@ public async Task DeleteLastDeployedStack(string stackName, string projectName, try { if (string.IsNullOrEmpty(projectName)) - throw new FailedToUpdateLocalUserSettingsFileException("The Project Name is not defined."); + throw new FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode.FailedToUpdateLocalUserSettingsFile, "The Project Name is not defined."); if (string.IsNullOrEmpty(awsAccountId) || string.IsNullOrEmpty(awsRegion)) - throw new FailedToUpdateLocalUserSettingsFileException("The AWS Account Id or Region is not defined."); + throw new FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode.FailedToUpdateLocalUserSettingsFile, "The AWS Account Id or Region is not defined."); var localUserSettings = await GetLocalUserSettings(); var lastDeployedStack = localUserSettings?.LastDeployedStacks? @@ -135,7 +135,7 @@ public async Task DeleteLastDeployedStack(string stackName, string projectName, } catch (Exception ex) { - throw new FailedToUpdateLocalUserSettingsFileException($"Failed to update the local user settings file " + + throw new FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode.FailedToUpdateLocalUserSettingsFile, $"Failed to update the local user settings file " + $"to delete the stack '{stackName}'.", ex); } } @@ -148,9 +148,9 @@ public async Task CleanOrphanStacks(List deployedStacks, string projectN try { if (string.IsNullOrEmpty(projectName)) - throw new FailedToUpdateLocalUserSettingsFileException("The Project Name is not defined."); + throw new FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode.FailedToUpdateLocalUserSettingsFile, "The Project Name is not defined."); if (string.IsNullOrEmpty(awsAccountId) || string.IsNullOrEmpty(awsRegion)) - throw new FailedToUpdateLocalUserSettingsFileException("The AWS Account Id or Region is not defined."); + throw new FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode.FailedToUpdateLocalUserSettingsFile, "The AWS Account Id or Region is not defined."); var localUserSettings = await GetLocalUserSettings(); var localStacks = localUserSettings?.LastDeployedStacks? @@ -167,7 +167,7 @@ public async Task CleanOrphanStacks(List deployedStacks, string projectN } catch (Exception ex) { - throw new FailedToUpdateLocalUserSettingsFileException($"Failed to update the local user settings file " + + throw new FailedToUpdateLocalUserSettingsFileException(DeployToolErrorCode.FailedToUpdateLocalUserSettingsFile, $"Failed to update the local user settings file " + $"to delete orphan stacks.", ex); } } @@ -204,7 +204,7 @@ private async Task WriteLocalUserSettingsFile(LocalUserSettings deployme } catch (Exception ex) { - throw new InvalidLocalUserSettingsFileException("The Local User Settings file is invalid.", ex); + throw new InvalidLocalUserSettingsFileException(DeployToolErrorCode.InvalidLocalUserSettingsFile, "The Local User Settings file is invalid.", ex); } } diff --git a/src/AWS.Deploy.Orchestration/Orchestrator.cs b/src/AWS.Deploy.Orchestration/Orchestrator.cs index b24fe7cd0..9b60caf92 100644 --- a/src/AWS.Deploy.Orchestration/Orchestrator.cs +++ b/src/AWS.Deploy.Orchestration/Orchestrator.cs @@ -107,7 +107,7 @@ public async Task> GenerateRecommendationsFromSavedDeployme if (_directoryManager == null) throw new InvalidOperationException($"{nameof(_directoryManager)} is null as part of the orchestartor object"); if (!_directoryManager.Exists(deploymentProjectPath)) - throw new InvalidCliArgumentException($"The path '{deploymentProjectPath}' does not exists on the file system. Please provide a valid deployment project path and try again."); + throw new InvalidCliArgumentException(DeployToolErrorCode.DeploymentProjectPathNotFound, $"The path '{deploymentProjectPath}' does not exists on the file system. Please provide a valid deployment project path and try again."); var engine = new RecommendationEngine.RecommendationEngine(new List { deploymentProjectPath }, _session); var additionalReplacements = await GetReplacements(); @@ -199,7 +199,7 @@ public async Task CreateContainerDeploymentBundle(CloudApplication cloudAp } catch (DockerEngineExceptionBase ex) { - throw new FailedToGenerateDockerFileException("Failed to generate a docker file", ex); + throw new FailedToGenerateDockerFileException(DeployToolErrorCode.FailedToGenerateDockerFile, "Failed to generate a docker file", ex); } } diff --git a/src/AWS.Deploy.Orchestration/RecipeHandler.cs b/src/AWS.Deploy.Orchestration/RecipeHandler.cs index 27721f83b..785b925be 100644 --- a/src/AWS.Deploy.Orchestration/RecipeHandler.cs +++ b/src/AWS.Deploy.Orchestration/RecipeHandler.cs @@ -51,7 +51,7 @@ public static async Task> GetRecipeDefinitions(ICustomRec } catch(IOException) { - throw new NoRecipeDefinitionsFoundException("Failed to find recipe definitions"); + throw new NoRecipeDefinitionsFoundException(DeployToolErrorCode.FailedToFindRecipeDefinitions, "Failed to find recipe definitions"); } return recipeDefinitions; diff --git a/src/AWS.Deploy.Orchestration/RecommendationEngine/RecommendationEngine.cs b/src/AWS.Deploy.Orchestration/RecommendationEngine/RecommendationEngine.cs index 6d12b199c..4002c8bb4 100644 --- a/src/AWS.Deploy.Orchestration/RecommendationEngine/RecommendationEngine.cs +++ b/src/AWS.Deploy.Orchestration/RecommendationEngine/RecommendationEngine.cs @@ -101,10 +101,10 @@ public List GetDeploymentBundleSettings(DeploymentBundleTypes } catch(IOException) { - throw new NoDeploymentBundleDefinitionsFoundException("Failed to find a deployment bundle definition"); + throw new NoDeploymentBundleDefinitionsFoundException(DeployToolErrorCode.DeploymentBundleDefinitionNotFound, "Failed to find a deployment bundle definition"); } - throw new NoDeploymentBundleDefinitionsFoundException("Failed to find a deployment bundle definition"); + throw new NoDeploymentBundleDefinitionsFoundException(DeployToolErrorCode.DeploymentBundleDefinitionNotFound, "Failed to find a deployment bundle definition"); } public async Task EvaluateRules(IList rules) @@ -125,7 +125,7 @@ public async Task EvaluateRules(IList rules { if(!availableTests.TryGetValue(test.Type, out var testInstance)) { - throw new InvalidRecipeDefinitionException($"Invalid test type [{test.Type}] found in rule."); + throw new InvalidRecipeDefinitionException(DeployToolErrorCode.RuleHasInvalidTestType, $"Invalid test type [{test.Type}] found in rule."); } var input = new RecommendationTestInput( diff --git a/src/AWS.Deploy.Orchestration/SystemCapabilityEvaluator.cs b/src/AWS.Deploy.Orchestration/SystemCapabilityEvaluator.cs index e13baf533..390948099 100644 --- a/src/AWS.Deploy.Orchestration/SystemCapabilityEvaluator.cs +++ b/src/AWS.Deploy.Orchestration/SystemCapabilityEvaluator.cs @@ -50,7 +50,7 @@ await _commandLineWrapper.Run( processExitCode = proc.ExitCode; containerType = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? proc.StandardOut?.TrimEnd('\n') ?? - throw new DockerInfoException("Failed to check if Docker is running in Windows or Linux container mode.") : + throw new DockerInfoException(DeployToolErrorCode.FailedToCheckDockerInfo, "Failed to check if Docker is running in Windows or Linux container mode.") : "linux"; }); diff --git a/src/AWS.Deploy.Orchestration/TemplateEngine.cs b/src/AWS.Deploy.Orchestration/TemplateEngine.cs index 361737624..b27a1b5d5 100644 --- a/src/AWS.Deploy.Orchestration/TemplateEngine.cs +++ b/src/AWS.Deploy.Orchestration/TemplateEngine.cs @@ -46,7 +46,7 @@ public void GenerateCDKProjectFromTemplate(Recommendation recommendation, Orches //The location of the base template that will be installed into the templating engine var cdkProjectTemplateDirectory = Path.Combine( Path.GetDirectoryName(recommendation.Recipe.RecipePath) ?? - throw new InvalidRecipePathException($"The following RecipePath is invalid as we could not retrieve the parent directory: {recommendation.Recipe.RecipePath}"), + throw new InvalidRecipePathException(DeployToolErrorCode.BaseTemplatesInvalidPath, $"The following RecipePath is invalid as we could not retrieve the parent directory: {recommendation.Recipe.RecipePath}"), recommendation.Recipe.CdkProjectTemplate); //Installing the base template into the templating engine to make it available for generation @@ -69,7 +69,7 @@ public void GenerateCDKProjectFromTemplate(Recommendation recommendation, Orches // CDK Template projects can parameterize the version number of the AWS.Deploy.Recipes.CDK.Common package. This avoid // projects having to be modified every time the package version is bumped. { "AWSDeployRecipesCDKCommonVersion", FileVersionInfo.GetVersionInfo(typeof(Constants.CloudFormationIdentifier).Assembly.Location).ProductVersion - ?? throw new InvalidAWSDeployRecipesCDKCommonVersionException("The version number of the AWS.Deploy.Recipes.CDK.Common package is invalid.") } + ?? throw new InvalidAWSDeployRecipesCDKCommonVersionException(DeployToolErrorCode.InvalidAWSDeployRecipesCDKCommonVersion, "The version number of the AWS.Deploy.Recipes.CDK.Common package is invalid.") } }; try @@ -82,7 +82,7 @@ public void GenerateCDKProjectFromTemplate(Recommendation recommendation, Orches } catch { - throw new TemplateGenerationFailedException("Failed to generate CDK project from template"); + throw new TemplateGenerationFailedException(DeployToolErrorCode.FailedToGenerateCDKProjectFromTemplate, "Failed to generate CDK project from template"); } } @@ -97,7 +97,7 @@ private void InstallTemplates(string folderLocation) } catch(Exception e) { - throw new DefaultTemplateInstallationFailedException("Failed to install the default template that is required to the generate the CDK project", e); + throw new DefaultTemplateInstallationFailedException(DeployToolErrorCode.FailedToInstallProjectTemplates, "Failed to install the default template that is required to the generate the CDK project", e); } } diff --git a/src/AWS.Deploy.Orchestration/Utilities/TemplateMetadataReader.cs b/src/AWS.Deploy.Orchestration/Utilities/TemplateMetadataReader.cs index d9a87cff0..08dc4035b 100644 --- a/src/AWS.Deploy.Orchestration/Utilities/TemplateMetadataReader.cs +++ b/src/AWS.Deploy.Orchestration/Utilities/TemplateMetadataReader.cs @@ -78,7 +78,7 @@ private static CloudApplicationMetadata ReadSettings(string templateBody) } catch(Exception e) { - throw new ParsingExistingCloudApplicationMetadataException("Error parsing existing application's metadata", e); + throw new ParsingExistingCloudApplicationMetadataException(DeployToolErrorCode.ErrorParsingApplicationMetadata, "Error parsing existing application's metadata", e); } } diff --git a/src/AWS.Deploy.Orchestration/Utilities/ZipFileManager.cs b/src/AWS.Deploy.Orchestration/Utilities/ZipFileManager.cs index 79ec963e6..3c945109e 100644 --- a/src/AWS.Deploy.Orchestration/Utilities/ZipFileManager.cs +++ b/src/AWS.Deploy.Orchestration/Utilities/ZipFileManager.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; +using AWS.Deploy.Common; using AWS.Deploy.Common.IO; namespace AWS.Deploy.Orchestration.Utilities @@ -48,7 +49,7 @@ private async Task BuildZipForLinux(string sourceDirectoryName, string destinati var zipCLI = FindExecutableInPath("zip"); if (string.IsNullOrEmpty(zipCLI)) - throw new FailedToCreateZipFileException("Failed to find the \"zip\" utility program in path. This program is required to maintain Linux file permissions in the zip archive."); + throw new FailedToCreateZipFileException(DeployToolErrorCode.FailedToFindZipUtility, "Failed to find the \"zip\" utility program in path. This program is required to maintain Linux file permissions in the zip archive."); var args = new StringBuilder($"\"{destinationArchiveFileName}\""); @@ -61,7 +62,7 @@ private async Task BuildZipForLinux(string sourceDirectoryName, string destinati var command = $"{zipCLI} {args}"; var result = await _commandLineWrapper.TryRunWithResult(command, sourceDirectoryName); if (result.ExitCode != 0) - throw new FailedToCreateZipFileException("\"zip\" utility program has failed to create a zip archive."); + throw new FailedToCreateZipFileException(DeployToolErrorCode.ZipUtilityFailedToZip, "\"zip\" utility program has failed to create a zip archive."); } /// diff --git a/src/AWS.Deploy.ServerMode.Client/RestAPI.cs b/src/AWS.Deploy.ServerMode.Client/RestAPI.cs index 1bb2eb449..1be411a07 100644 --- a/src/AWS.Deploy.ServerMode.Client/RestAPI.cs +++ b/src/AWS.Deploy.ServerMode.Client/RestAPI.cs @@ -1476,6 +1476,18 @@ public enum DeploymentStatus } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.1.0 (Newtonsoft.Json v12.0.0.0)")] + public partial class DeployToolExceptionSummary + { + [Newtonsoft.Json.JsonProperty("errorCode", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string ErrorCode { get; set; } + + [Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string Message { get; set; } + + + } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.4.1.0 (Newtonsoft.Json v12.0.0.0)")] public partial class DisplayedResourceSummary { @@ -1567,6 +1579,9 @@ public partial class GetDeploymentStatusOutput [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] public DeploymentStatus Status { get; set; } + [Newtonsoft.Json.JsonProperty("exception", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public DeployToolExceptionSummary Exception { get; set; } + } diff --git a/version.json b/version.json index 710f7f584..2848fe8f8 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.29", + "version": "0.30", "publicReleaseRefSpec": [ ".*" ],