From a344f9e6d05055adbf379e48ffd6b74b44e4fb61 Mon Sep 17 00:00:00 2001 From: aws-sdk-dotnet-automation Date: Wed, 28 Sep 2022 16:19:06 +0000 Subject: [PATCH 1/3] build: version bump to 1.5 --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 0429ca732..084f46426 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": "1.4", + "version": "1.5", "publicReleaseRefSpec": [ ".*" ], From af3530c0ef95e23d1f8509bf8f5d669a1442a560 Mon Sep 17 00:00:00 2001 From: Malhar Khimsaria Date: Thu, 29 Sep 2022 09:10:05 -0700 Subject: [PATCH 2/3] chore: Truncate long error messages --- .../Controllers/DeploymentController.cs | 5 +- src/AWS.Deploy.Common/Exceptions.cs | 22 +++++++ .../ExceptionExtensionsTests.cs | 66 +++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 test/AWS.Deploy.CLI.UnitTests/ExceptionExtensionsTests.cs diff --git a/src/AWS.Deploy.CLI/ServerMode/Controllers/DeploymentController.cs b/src/AWS.Deploy.CLI/ServerMode/Controllers/DeploymentController.cs index b128192a9..6764d7329 100644 --- a/src/AWS.Deploy.CLI/ServerMode/Controllers/DeploymentController.cs +++ b/src/AWS.Deploy.CLI/ServerMode/Controllers/DeploymentController.cs @@ -615,13 +615,14 @@ public IActionResult GetDeploymentStatus(string sessionId) if (state.DeploymentTask.Exception != null) { var innerException = state.DeploymentTask.Exception.InnerException; + var message = innerException.GetTruncatedErrorMessage(); if (innerException is DeployToolException deployToolException) { - output.Exception = new DeployToolExceptionSummary(deployToolException.ErrorCode.ToString(), deployToolException.Message, deployToolException.ProcessExitCode); + output.Exception = new DeployToolExceptionSummary(deployToolException.ErrorCode.ToString(), message, deployToolException.ProcessExitCode); } else { - output.Exception = new DeployToolExceptionSummary(DeployToolErrorCode.UnexpectedError.ToString(), innerException?.Message ?? string.Empty); + output.Exception = new DeployToolExceptionSummary(DeployToolErrorCode.UnexpectedError.ToString(), message); } } } diff --git a/src/AWS.Deploy.Common/Exceptions.cs b/src/AWS.Deploy.Common/Exceptions.cs index 3e18a207e..c273410d6 100644 --- a/src/AWS.Deploy.Common/Exceptions.cs +++ b/src/AWS.Deploy.Common/Exceptions.cs @@ -318,5 +318,27 @@ public static string PrettyPrint(this Exception? e) return $"{Environment.NewLine}{e.Message}{Environment.NewLine}{e.StackTrace}{PrettyPrint(e.InnerException)}"; } + + /// + /// Returns the truncated error message by only preserving the leading and trailing k characters. + /// The message will only be truncated if its length is greater than 2*k. + /// + /// Species the number of leading and trailing characters to preserve. + /// The truncated error message or if the error message is null or empty. + public static string GetTruncatedErrorMessage(this Exception? e, int numChars = 500) + { + var message = e?.Message; + + if (string.IsNullOrEmpty(message)) + return string.Empty; + + if (message.Length <= 2*numChars) + return message; + + var firstKChars = message.Substring(0, numChars); + var lastkChars = message.Substring(message.Length - numChars); + var seperator = $"{Environment.NewLine}...{Environment.NewLine}Error truncated to the first and last {numChars} characters{Environment.NewLine}...{Environment.NewLine}"; + return $"{firstKChars}{seperator}{lastkChars}"; + } } } diff --git a/test/AWS.Deploy.CLI.UnitTests/ExceptionExtensionsTests.cs b/test/AWS.Deploy.CLI.UnitTests/ExceptionExtensionsTests.cs new file mode 100644 index 000000000..f44b006d7 --- /dev/null +++ b/test/AWS.Deploy.CLI.UnitTests/ExceptionExtensionsTests.cs @@ -0,0 +1,66 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +using System; +using AWS.Deploy.Common; +using Xunit; + +namespace AWS.Deploy.CLI.UnitTests +{ + public class ExceptionExtensionsTests + { + [Fact] + public void GetTruncatedErrorMessage_EmptyMessage() + { + // ARRANGE + var ex = new Exception(string.Empty); + + // ACT and ASSERT + Assert.Equal(string.Empty, ex.GetTruncatedErrorMessage()); + } + + [Fact] + public void GetTruncatedErrorMessage_NoTruncation() + { + // ARRANGE + var message = "This is an AWSDeployToolException"; + var ex = new Exception(message); + + // ACT and ASSERT + // No truncation is performed because the message length < 2 * k + Assert.Equal(message, ex.GetTruncatedErrorMessage(numChars: 50)); + } + + [Fact] + public void GetTruncatedErrorMessage() + { + // ARRANGE + var message = + "error text. error text. error text. error text. error text. " + + "error text. error text. error text. error text. error text. error text. " + + "error text. error text. error text. error text. error text. error text. " + + "error text. error text. error text. error text. error text. error text. " + + "error text. error text. error text. error text. error text. error text. " + + "error text. error text. error text. error text. error text. error text. " + + "error text. error text. error text. error text. error text. error text. " + + "error text. error text. error text. error text. error text. error text. " + + "error text. error text. error text. error text. error text. error text. error text."; + + + var ex = new Exception(message); + + // ACT + var truncatedErrorMessage = ex.GetTruncatedErrorMessage(numChars: 50); + + // ACT and ASSERT + var expectedMessage = + @"error text. error text. error text. error text. er +... +Error truncated to the first and last 50 characters +... +t. error text. error text. error text. error text."; + + Assert.Equal(expectedMessage, truncatedErrorMessage); + } + } +} From a7ffa90cc522a4dfc4344495c4442f78fce5ecb1 Mon Sep 17 00:00:00 2001 From: Philippe El Asmar Date: Tue, 4 Oct 2022 09:25:54 -0400 Subject: [PATCH 3/3] fix: json comments in beanstalk windows manifest caused exception --- src/AWS.Deploy.Common/Exceptions.cs | 3 +- src/AWS.Deploy.Orchestration/Exceptions.cs | 8 ++++ .../AWSElasticBeanstalkHandler.cs | 2 +- .../Utilities/DeployedApplicationQueryer.cs | 36 +++++++++++--- .../DeployedApplicationQueryerTests.cs | 48 ++++++++++++------- 5 files changed, 72 insertions(+), 25 deletions(-) diff --git a/src/AWS.Deploy.Common/Exceptions.cs b/src/AWS.Deploy.Common/Exceptions.cs index c273410d6..b9644f28f 100644 --- a/src/AWS.Deploy.Common/Exceptions.cs +++ b/src/AWS.Deploy.Common/Exceptions.cs @@ -125,7 +125,8 @@ public enum DeployToolErrorCode ECRRepositoryNameIsNull = 10010300, FailedToReadCdkBootstrapVersion = 10010400, UnsupportedOptionSettingType = 10010500, - FailedToSaveDeploymentSettings = 10010600 + FailedToSaveDeploymentSettings = 10010600, + InvalidWindowsManifestFile = 10010700 } public class ProjectFileNotFoundException : DeployToolException diff --git a/src/AWS.Deploy.Orchestration/Exceptions.cs b/src/AWS.Deploy.Orchestration/Exceptions.cs index 0ad3fa874..72a94c2e4 100644 --- a/src/AWS.Deploy.Orchestration/Exceptions.cs +++ b/src/AWS.Deploy.Orchestration/Exceptions.cs @@ -260,4 +260,12 @@ public class InvalidDeployToolWorkspaceException : DeployToolException { public InvalidDeployToolWorkspaceException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } } + + /// + /// Throw if the deploy tool detects an invalid windows elastic beanstalk manifest file. + /// + public class InvalidWindowsManifestFileException : DeployToolException + { + public InvalidWindowsManifestFileException(DeployToolErrorCode errorCode, string message, Exception? innerException = null) : base(errorCode, message, innerException) { } + } } diff --git a/src/AWS.Deploy.Orchestration/ServiceHandlers/AWSElasticBeanstalkHandler.cs b/src/AWS.Deploy.Orchestration/ServiceHandlers/AWSElasticBeanstalkHandler.cs index 3189bb556..3baccb316 100644 --- a/src/AWS.Deploy.Orchestration/ServiceHandlers/AWSElasticBeanstalkHandler.cs +++ b/src/AWS.Deploy.Orchestration/ServiceHandlers/AWSElasticBeanstalkHandler.cs @@ -93,7 +93,7 @@ public AWSElasticBeanstalkHandler(IAWSClientFactory awsClientFactory, IOrchestra { try { - return JsonSerializer.Deserialize(json?.ToString() ?? string.Empty); + return JsonSerializer.Deserialize(json?.ToString() ?? string.Empty, new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Skip }); } catch { diff --git a/src/AWS.Deploy.Orchestration/Utilities/DeployedApplicationQueryer.cs b/src/AWS.Deploy.Orchestration/Utilities/DeployedApplicationQueryer.cs index 9d25e4d3e..14e9a0087 100644 --- a/src/AWS.Deploy.Orchestration/Utilities/DeployedApplicationQueryer.cs +++ b/src/AWS.Deploy.Orchestration/Utilities/DeployedApplicationQueryer.cs @@ -270,20 +270,42 @@ private async Task> GetBeanstalkEnvironmentConfigura } if (recipeId.Equals(Constants.RecipeIdentifier.EXISTING_BEANSTALK_WINDOWS_ENVIRONMENT_RECIPE_ID)) + { + var windowsManifest = await GetBeanstalkWindowsManifest(projectPath); + if (windowsManifest != null && windowsManifest.Deployments.AspNetCoreWeb.Count != 0) + { + optionSettings[Constants.ElasticBeanstalk.IISWebSiteOptionId] = windowsManifest.Deployments.AspNetCoreWeb[0].Parameters.IISWebSite; + optionSettings[Constants.ElasticBeanstalk.IISAppPathOptionId] = windowsManifest.Deployments.AspNetCoreWeb[0].Parameters.IISPath; + } + } + + return optionSettings; + } + + private async Task GetBeanstalkWindowsManifest(string projectPath) + { + try { var manifestPath = Path.Combine(Path.GetDirectoryName(projectPath) ?? string.Empty, Constants.ElasticBeanstalk.WindowsManifestName); if (_fileManager.Exists(manifestPath)) { - var manifest = JsonSerializer.Deserialize(await _fileManager.ReadAllTextAsync(manifestPath)); - if (manifest.Deployments.AspNetCoreWeb.Count != 0) + var manifest = JsonSerializer.Deserialize(await _fileManager.ReadAllTextAsync(manifestPath), new JsonSerializerOptions { - optionSettings[Constants.ElasticBeanstalk.IISWebSiteOptionId] = manifest.Deployments.AspNetCoreWeb[0].Parameters.IISWebSite; - optionSettings[Constants.ElasticBeanstalk.IISAppPathOptionId] = manifest.Deployments.AspNetCoreWeb[0].Parameters.IISPath; - } + ReadCommentHandling = JsonCommentHandling.Skip + }); + + return manifest; } - } - return optionSettings; + return null; + } + catch (Exception ex) + { + throw new InvalidWindowsManifestFileException( + DeployToolErrorCode.InvalidWindowsManifestFile, + $"We detected a malformed Elastic Beanstalk Windows manifest file '{Constants.ElasticBeanstalk.WindowsManifestName}' in your project and were not able to load the previous settings from that file.", + ex); + } } private ConfigurationOptionSetting GetBeanstalkEnvironmentConfigurationSetting(List configurationSettings, string optionNameSpace, string optionName) diff --git a/test/AWS.Deploy.Orchestration.UnitTests/DeployedApplicationQueryerTests.cs b/test/AWS.Deploy.Orchestration.UnitTests/DeployedApplicationQueryerTests.cs index b9f90e7db..c744cf342 100644 --- a/test/AWS.Deploy.Orchestration.UnitTests/DeployedApplicationQueryerTests.cs +++ b/test/AWS.Deploy.Orchestration.UnitTests/DeployedApplicationQueryerTests.cs @@ -471,8 +471,38 @@ public async Task GetPreviousSettings_BeanstalkEnvironment() Assert.Equal("false", optionSettings[Constants.ElasticBeanstalk.XRayTracingOptionId]); } - [Fact] - public async Task GetPreviousSettings_BeanstalkWindowsEnvironment() + [Theory] + + [InlineData(@"{ + ""manifestVersion"": 1, + ""deployments"": { + ""aspNetCoreWeb"": [ + { + ""name"": ""app"", + ""parameters"": { + ""iisPath"": ""/path"", + ""iisWebSite"": ""Default Web Site Custom"" + } + } + ] + } + }")] + [InlineData(@"{ + ""manifestVersion"": 1, + // comments + ""deployments"": { + ""aspNetCoreWeb"": [ + { + ""name"": ""app"", + ""parameters"": { + ""iisPath"": ""/path"", + ""iisWebSite"": ""Default Web Site Custom"" + } + } + ] + } + }")] + public async Task GetPreviousSettings_BeanstalkWindowsEnvironment(string manifestJson) { var application = new CloudApplication("name", "Id", CloudApplicationResourceType.BeanstalkEnvironment, "recipe"); var configurationSettings = new List @@ -507,20 +537,6 @@ public async Task GetPreviousSettings_BeanstalkWindowsEnvironment() _mockOrchestratorInteractiveService.Object, _fileManager); - var manifestJson = @"{ - ""manifestVersion"": 1, - ""deployments"": { - ""aspNetCoreWeb"": [ - { - ""name"": ""app"", - ""parameters"": { - ""iisPath"": ""/path"", - ""iisWebSite"": ""Default Web Site Custom"" - } - } - ] - } - }"; _fileManager.InMemoryStore.Add(Path.Combine("testPath", "aws-windows-deployment-manifest.json"), manifestJson); var projectDefinition = new ProjectDefinition(null, Path.Combine("testPath", "project.csproj"), "", "net6.0"); var recipeDefinitiion = new RecipeDefinition("AspNetAppExistingBeanstalkWindowsEnvironment", "", "", DeploymentTypes.BeanstalkEnvironment, DeploymentBundleTypes.DotnetPublishZipFile, "", "", "", "", "");