From e22453fcc6c4bca3d075b139640b94f5f57644ae Mon Sep 17 00:00:00 2001 From: Norm Johanson Date: Tue, 9 Sep 2025 23:49:59 -0700 Subject: [PATCH 1/3] Updated to V4 of the AWS SDK for .NET --- .../7107587d-3fa8-4094-aa23-7b1d1f92b562.json | 30 +++ .../Amazon.Common.DotNetCli.Tools.csproj | 24 +- .../Commands/BaseCommand.cs | 47 ++-- .../Commands/BasePushDockerImageCommand.cs | 12 +- .../DefaultConfigFile.cs | 114 ++++----- .../DockerCLIWrapper.cs | 12 +- .../ExtensionMethods.cs | 44 +++- .../RoleHelper.cs | 81 ++++--- .../Utilities.cs | 24 +- src/Amazon.ECS.Tools/Amazon.ECS.Tools.csproj | 24 +- .../Commands/CommandProperties.cs | 13 +- .../Commands/DeployScheduledTaskCommand.cs | 3 +- .../Commands/DeployServiceCommand.cs | 12 +- .../Commands/DeployTaskCommand.cs | 16 +- .../Commands/ECSBaseCommand.cs | 1 - .../Commands/PushDockerImageCommand.cs | 1 - src/Amazon.ECS.Tools/ECSToolsDefaults.cs | 6 +- src/Amazon.ECS.Tools/ECSUtilities.cs | 227 +++++++++++------- .../Amazon.ElasticBeanstalk.Tools.csproj | 18 +- .../Commands/CommandProperties.cs | 5 +- .../Commands/DeleteEnvironmentCommand.cs | 3 +- .../Commands/DeployEnvironmentCommand.cs | 44 ++-- .../Commands/EBBaseCommand.cs | 2 + .../Commands/ListEnvironmentsCommand.cs | 14 +- .../Commands/PackageCommand.cs | 3 +- .../EBUtilities.cs | 76 +++--- .../ElasticBeanstalkToolsDefaults.cs | 7 +- .../Amazon.Lambda.Tools.csproj | 20 +- .../Commands/DeleteFunctionCommand.cs | 4 +- .../Commands/DeleteLayerVersionCommand.cs | 4 +- .../Commands/DeleteServerlessCommand.cs | 4 +- .../Commands/DeployFunctionCommand.cs | 6 +- .../Commands/DeployServerlessCommand.cs | 21 +- .../Commands/GetFunctionConfigCommand.cs | 8 +- .../Commands/GetLayerVersionDetailsCommand.cs | 6 +- .../Commands/InvokeFunctionCommand.cs | 4 +- .../Commands/ListFunctionCommand.cs | 12 +- .../Commands/ListLayerVersionsCommand.cs | 4 +- .../Commands/ListLayersCommand.cs | 23 +- .../Commands/ListServerlessCommand.cs | 7 +- .../Commands/PackageCICommand.cs | 3 +- .../Commands/PackageCommand.cs | 3 +- .../Commands/PublishLayerCommand.cs | 10 +- .../Commands/PushDockerImageCommand.cs | 5 +- .../Commands/UpdateFunctionConfigCommand.cs | 5 +- src/Amazon.Lambda.Tools/LambdaPackager.cs | 134 ++++++----- .../LambdaToolsDefaults.cs | 15 +- src/Amazon.Lambda.Tools/LambdaUtilities.cs | 83 +++---- .../TemplateProcessor/ITemplateParser.cs | 5 - .../TemplateProcessor/JsonTemplateParser.cs | 92 ++++--- .../CommandLineParserTest.cs | 2 +- .../OptionParseTests.cs | 12 +- .../UtilitiesTests.cs | 2 +- .../ECSToolsDefaultsReaderTest.cs | 4 +- .../Amazon.ElasticBeanstalk.Tools.Test.csproj | 2 +- .../DeployTests.cs | 44 +++- .../Amazon.Lambda.Tools.Test.csproj | 6 +- .../CommandLineParserTest.cs | 2 +- test/Amazon.Lambda.Tools.Test/DeployTest.cs | 48 ++-- test/Amazon.Lambda.Tools.Test/LayerTests.cs | 12 +- .../OptionParseTests.cs | 12 +- .../TemplateParserTests.cs | 37 ++- .../TemplateSubsitutionTests.cs | 2 +- .../UtilitiesTests.cs | 4 +- testapps/HelloWorldWebApp/Dockerfile | 2 +- .../HelloWorldWebApp/HelloWorldWebApp.csproj | 12 +- testapps/HelloWorldWebApp/Program.cs | 32 +-- testapps/HelloWorldWebApp/Startup.cs | 35 --- .../serverless-resource-arm.template | 48 ++++ testapps/TestBeanstalkWebApp/Program.cs | 31 +-- testapps/TestBeanstalkWebApp/Startup.cs | 35 --- .../TestBeanstalkWebApp.csproj | 2 +- .../TestLayerAspNetCore.csproj | 6 +- testapps/TestLayerAspNetCore/fake.template | 2 +- .../TestLayerExample/TestLayerExample.csproj | 6 +- .../TestLayerServerless.csproj | 8 +- 76 files changed, 937 insertions(+), 812 deletions(-) create mode 100644 .autover/changes/7107587d-3fa8-4094-aa23-7b1d1f92b562.json delete mode 100644 testapps/HelloWorldWebApp/Startup.cs create mode 100644 testapps/ImageBasedProjects/ServerlessTemplateExamples/serverless-resource-arm.template delete mode 100644 testapps/TestBeanstalkWebApp/Startup.cs diff --git a/.autover/changes/7107587d-3fa8-4094-aa23-7b1d1f92b562.json b/.autover/changes/7107587d-3fa8-4094-aa23-7b1d1f92b562.json new file mode 100644 index 00000000..8223afd3 --- /dev/null +++ b/.autover/changes/7107587d-3fa8-4094-aa23-7b1d1f92b562.json @@ -0,0 +1,30 @@ +{ + "Projects": [ + { + "Name": "Amazon.ECS.Tools", + "Type": "Major", + "ChangelogMessages": [ + "Updated to V4 of the AWS SDK for .NET", + "Updated the minimum requirement from .NET Core 3.1 to .NET 6" + ] + }, + { + "Name": "Amazon.ElasticBeanstalk.Tools", + "Type": "Major", + "ChangelogMessages": [ + "Updated to V4 of the AWS SDK for .NET", + "Updated the minimum requirement from .NET Core 3.1 to .NET 6" + ] + }, + { + "Name": "Amazon.Lambda.Tools", + "Type": "Major", + "ChangelogMessages": [ + "Updated to V4 of the AWS SDK for .NET", + "Updated the minimum requirement from .NET Core 3.1 to .NET 6", + "Fixed \"The image manifest or layer media type for the source image is not supported.\" issue when container image was built for a Lambda function by adding the \"--provenance=false\" switch for the docker buildx command" + ] + } + + ] +} \ No newline at end of file diff --git a/src/Amazon.Common.DotNetCli.Tools/Amazon.Common.DotNetCli.Tools.csproj b/src/Amazon.Common.DotNetCli.Tools/Amazon.Common.DotNetCli.Tools.csproj index 8500202a..8661764e 100644 --- a/src/Amazon.Common.DotNetCli.Tools/Amazon.Common.DotNetCli.Tools.csproj +++ b/src/Amazon.Common.DotNetCli.Tools/Amazon.Common.DotNetCli.Tools.csproj @@ -1,19 +1,25 @@  - netcoreapp3.1;net6.0 + net6.0;net8.0 + + 7.3 - - - - - - - + + + + + + + - 1701;1702;1705;1591 + true + 1701;1702;1705;1591;NETSDK1138 \ No newline at end of file diff --git a/src/Amazon.Common.DotNetCli.Tools/Commands/BaseCommand.cs b/src/Amazon.Common.DotNetCli.Tools/Commands/BaseCommand.cs index 0312a34f..a431e006 100644 --- a/src/Amazon.Common.DotNetCli.Tools/Commands/BaseCommand.cs +++ b/src/Amazon.Common.DotNetCli.Tools/Commands/BaseCommand.cs @@ -7,14 +7,15 @@ using System.Linq; using System.Reflection; using System.Text; +using System.Text.Json; using System.Threading.Tasks; -using ThirdParty.Json.LitJson; using Amazon.ECR; using Amazon.IdentityManagement; using Amazon.IdentityManagement.Model; using Amazon.S3; using Amazon.SecurityToken; +using Amazon.Runtime.Credentials; namespace Amazon.Common.DotNetCli.Tools.Commands { @@ -36,7 +37,7 @@ public BaseCommand(IToolLogger logger, string workingDirectory) public BaseCommand(IToolLogger logger, string workingDirectory, IList possibleOptions, string[] args) : this(logger, workingDirectory) { - args = args ?? new string[0]; + args = args ?? Array.Empty(); this.OriginalCommandLineArguments = args; var values = CommandLineParser.ParseArguments(possibleOptions, args); ParseCommandArguments(values); @@ -250,12 +251,12 @@ protected AWSCredentials DetermineAWSCredentials() var chain = new CredentialProfileStoreChain(this.ProfileLocation); if (!chain.TryGetAWSCredentials(profile, out this._resolvedCredentials)) { - this._resolvedCredentials = FallbackCredentialsFactory.GetCredentials(); + this._resolvedCredentials = DefaultAWSCredentialsIdentityResolver.GetCredentials(); } } else { - this._resolvedCredentials = FallbackCredentialsFactory.GetCredentials(); + this._resolvedCredentials = DefaultAWSCredentialsIdentityResolver.GetCredentials(); } if(this._resolvedCredentials is AssumeRoleAWSCredentials) @@ -384,12 +385,12 @@ public string GetRoleValueOrDefault(string propertyValue, CommandOption option, } /// - /// Complex parameters are formatted as a JSON string. This method parses the string into the JsonData object + /// Complex parameters are formatted as a JSON string. This method parses the string into the JsonElement object /// /// /// /// - public JsonData GetJsonValueOrDefault(string propertyValue, CommandOption option) + public JsonElement? GetJsonValueOrDefault(string propertyValue, CommandOption option) { string jsonContent = GetStringValueOrDefault(propertyValue, option, false); if (string.IsNullOrWhiteSpace(jsonContent)) @@ -397,8 +398,10 @@ public JsonData GetJsonValueOrDefault(string propertyValue, CommandOption option try { - var data = JsonMapper.ToObject(jsonContent); - return data; + using (JsonDocument doc = JsonDocument.Parse(jsonContent)) + { + return doc.RootElement.Clone(); + } } catch(Exception e) { @@ -697,7 +700,7 @@ protected string PromptForValue(CommandOption option) return cachedValue; } - string input = null; + string input; Console.Out.WriteLine($"Enter {option.Name}: ({option.Description})"); @@ -838,29 +841,27 @@ protected void SaveConfigFile() { try { - JsonData data; + var data = new Dictionary(); if (File.Exists(this.DefaultConfig.SourceFile)) { - data = JsonMapper.ToObject(File.ReadAllText(this.DefaultConfig.SourceFile)); - } - else - { - data = new JsonData(); + string existingJson = File.ReadAllText(this.DefaultConfig.SourceFile); + using (JsonDocument doc = JsonDocument.Parse(existingJson)) + { + foreach (JsonProperty prop in doc.RootElement.EnumerateObject()) + { + data[prop.Name] = prop.Value.GetJsonValue(); + } + } } data.SetIfNotNull(CommonDefinedCommandOptions.ARGUMENT_AWS_REGION.ConfigFileKey, this.GetStringValueOrDefault(this.Region, CommonDefinedCommandOptions.ARGUMENT_AWS_REGION, false)); data.SetIfNotNull(CommonDefinedCommandOptions.ARGUMENT_AWS_PROFILE.ConfigFileKey, this.GetStringValueOrDefault(this.Profile, CommonDefinedCommandOptions.ARGUMENT_AWS_PROFILE, false)); data.SetIfNotNull(CommonDefinedCommandOptions.ARGUMENT_AWS_PROFILE_LOCATION.ConfigFileKey, this.GetStringValueOrDefault(this.ProfileLocation, CommonDefinedCommandOptions.ARGUMENT_AWS_PROFILE_LOCATION, false)); - SaveConfigFile(data); - StringBuilder sb = new StringBuilder(); - JsonWriter writer = new JsonWriter(sb); - writer.PrettyPrint = true; - JsonMapper.ToJson(data, writer); - - var json = sb.ToString(); + var options = new JsonSerializerOptions { WriteIndented = true }; + var json = JsonSerializer.Serialize(data, options); File.WriteAllText(this.DefaultConfig.SourceFile, json); this.Logger?.WriteLine($"Config settings saved to {this.DefaultConfig.SourceFile}"); } @@ -870,7 +871,7 @@ protected void SaveConfigFile() } } - protected abstract void SaveConfigFile(JsonData data); + protected abstract void SaveConfigFile(Dictionary data); public bool ConfirmDeletion(string resource) { diff --git a/src/Amazon.Common.DotNetCli.Tools/Commands/BasePushDockerImageCommand.cs b/src/Amazon.Common.DotNetCli.Tools/Commands/BasePushDockerImageCommand.cs index 95067d53..1cdb044f 100644 --- a/src/Amazon.Common.DotNetCli.Tools/Commands/BasePushDockerImageCommand.cs +++ b/src/Amazon.Common.DotNetCli.Tools/Commands/BasePushDockerImageCommand.cs @@ -5,7 +5,6 @@ using Amazon.ECR.Model; using Amazon.ECR; -using ThirdParty.Json.LitJson; using System.IO; using Amazon.Common.DotNetCli.Tools.Options; using Amazon.Common.DotNetCli.Tools; @@ -338,7 +337,7 @@ private async Task SetupECRRepository(string ecrRepositoryName) } Repository repository; - if (describeResponse != null && describeResponse.Repositories.Count == 1) + if (describeResponse != null && describeResponse.Repositories != null && describeResponse.Repositories.Count == 1) { this.Logger?.WriteLine($"Found existing ECR Repository {ecrRepositoryName}"); repository = describeResponse.Repositories[0]; @@ -367,6 +366,11 @@ private async Task InitiateDockerLogin(DockerCLIWrapper dockerCLI) this.Logger?.WriteLine("Fetching ECR authorization token to use to login with the docker CLI"); var response = await this.ECRClient.GetAuthorizationTokenAsync(new GetAuthorizationTokenRequest()); + if (response.AuthorizationData == null || response.AuthorizationData.Count == 0) + { + throw new ToolsException("No authorization data returned from ECR", ToolsException.CommonErrorCode.GetECRAuthTokens); + } + var authTokenBytes = Convert.FromBase64String(response.AuthorizationData[0].AuthorizationToken); var authToken = Encoding.UTF8.GetString(authTokenBytes); var decodedTokens = authToken.Split(':'); @@ -387,7 +391,7 @@ private async Task InitiateDockerLogin(DockerCLIWrapper dockerCLI) } } - protected override void SaveConfigFile(JsonData data) + protected override void SaveConfigFile(Dictionary data) { this.PushDockerImageProperties.PersistSettings(this, data); } @@ -490,7 +494,7 @@ public void ParseCommandArguments(CommandOptions values) } - public void PersistSettings(BaseCommand command, JsonData data) + public void PersistSettings(BaseCommand command, Dictionary data) { data.SetIfNotNull(CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION.ConfigFileKey, command.GetStringValueOrDefault(this.Configuration, CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION, false)); data.SetIfNotNull(CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK.ConfigFileKey, command.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, false)); diff --git a/src/Amazon.Common.DotNetCli.Tools/DefaultConfigFile.cs b/src/Amazon.Common.DotNetCli.Tools/DefaultConfigFile.cs index 4c27dfe6..0b96cfbf 100644 --- a/src/Amazon.Common.DotNetCli.Tools/DefaultConfigFile.cs +++ b/src/Amazon.Common.DotNetCli.Tools/DefaultConfigFile.cs @@ -3,27 +3,27 @@ using System.Collections.Generic; using System.IO; using System.Text; -using ThirdParty.Json.LitJson; +using System.Text.Json; namespace Amazon.Common.DotNetCli.Tools { public abstract class DefaultConfigFile { - JsonData _rootData; + JsonElement _rootData; public DefaultConfigFile() - : this(new JsonData(), string.Empty) + : this(new JsonElement(), string.Empty) { } public DefaultConfigFile(string sourceFile) - : this(new JsonData(), sourceFile) + : this(new JsonElement(), sourceFile) { } - public DefaultConfigFile(JsonData data, string sourceFile) + public DefaultConfigFile(JsonElement data, string sourceFile) { - this._rootData = data ?? new JsonData(); + this._rootData = data; this.SourceFile = sourceFile; } @@ -47,17 +47,18 @@ public void LoadDefaults(string projectLocation, string configFile) if (!File.Exists(path)) return; - using (var reader = new StreamReader(File.OpenRead(path))) + try { - try + string json = File.ReadAllText(path); + using (JsonDocument doc = JsonDocument.Parse(json)) { - this._rootData = JsonMapper.ToObject(reader) as JsonData; - this.SourceFile = path; - } - catch (Exception e) - { - throw new ToolsException($"Error parsing default config {path}: {e.Message}", ToolsException.CommonErrorCode.DefaultsParseFail, e); + this._rootData = doc.RootElement.Clone(); } + this.SourceFile = path; + } + catch (Exception e) + { + throw new ToolsException($"Error parsing default config {path}: {e.Message}", ToolsException.CommonErrorCode.DefaultsParseFail, e); } } @@ -73,42 +74,45 @@ public object this[string fullSwitchName] if (fullSwitchName.StartsWith("--")) fullSwitchName = fullSwitchName.Substring(2); - if (this._rootData[fullSwitchName] == null) + if (_rootData.ValueKind == JsonValueKind.Undefined || !_rootData.TryGetProperty(fullSwitchName, out JsonElement element)) return null; - if (this._rootData[fullSwitchName].IsString) - return this._rootData[fullSwitchName].ToString(); - if (this._rootData[fullSwitchName].IsInt) - return (int)this._rootData[fullSwitchName]; - if (this._rootData[fullSwitchName].IsBoolean) - return (bool)this._rootData[fullSwitchName]; - if (this._rootData[fullSwitchName].IsArray) - { - var items = new string[this._rootData[fullSwitchName].Count]; - for (int i = 0; i < items.Length; i++) - { - items[i] = this._rootData[fullSwitchName][i].ToString(); - } - return items; - } - if (this._rootData[fullSwitchName].IsObject) + switch (element.ValueKind) { - var obj = new Dictionary(); - foreach (var key in this._rootData[fullSwitchName].PropertyNames) - { - obj[key] = this._rootData[key]?.ToString(); - } - return obj; + case JsonValueKind.String: + return element.GetString(); + case JsonValueKind.Number: + return element.GetInt32(); + case JsonValueKind.True: + case JsonValueKind.False: + return element.GetBoolean(); + case JsonValueKind.Array: + var items = new string[element.GetArrayLength()]; + int index = 0; + foreach (JsonElement item in element.EnumerateArray()) + { + items[index++] = item.ToString(); + } + return items; + case JsonValueKind.Object: + var obj = new Dictionary(); + foreach (JsonProperty prop in element.EnumerateObject()) + { + obj[prop.Name] = prop.Value.ToString(); + } + return obj; + default: + return null; } - - return null; } } - protected JsonData GetValue(CommandOption option) + protected JsonElement GetValue(CommandOption option) { var key = option.Switch.Substring(2); - return this._rootData[key]; + if (_rootData.ValueKind == JsonValueKind.Undefined || !_rootData.TryGetProperty(key, out JsonElement element)) + return new JsonElement(); + return element; } /// @@ -119,18 +123,21 @@ protected JsonData GetValue(CommandOption option) public string GetValueAsString(CommandOption option) { var key = option.Switch.Substring(2); - var data = this._rootData[key]; - if (data == null) + if (_rootData.ValueKind == JsonValueKind.Undefined || !_rootData.TryGetProperty(key, out JsonElement element)) return null; - if (data.IsString) - return data.ToString(); - else if (data.IsBoolean) - return ((bool)data).ToString(); - else if (data.IsInt) - return ((int)data).ToString(); - - return null; + switch (element.ValueKind) + { + case JsonValueKind.String: + return element.GetString(); + case JsonValueKind.True: + case JsonValueKind.False: + return element.GetBoolean().ToString(); + case JsonValueKind.Number: + return element.GetInt32().ToString(); + default: + return null; + } } @@ -159,11 +166,10 @@ public static string FormatKeyValue(IDictionary values) public string GetRawString(string key) { - var data = this._rootData[key]; - if (data == null || !data.IsString) + if (_rootData.ValueKind == JsonValueKind.Undefined || !_rootData.TryGetProperty(key, out JsonElement element) || element.ValueKind != JsonValueKind.String) return null; - return data.ToString(); + return element.GetString(); } } } diff --git a/src/Amazon.Common.DotNetCli.Tools/DockerCLIWrapper.cs b/src/Amazon.Common.DotNetCli.Tools/DockerCLIWrapper.cs index a7e8f713..84db409b 100644 --- a/src/Amazon.Common.DotNetCli.Tools/DockerCLIWrapper.cs +++ b/src/Amazon.Common.DotNetCli.Tools/DockerCLIWrapper.cs @@ -30,19 +30,13 @@ public int Build(string workingDirectory, string dockerFile, string imageTag, st var arguments = new StringBuilder(); -#if NETCOREAPP3_1_OR_GREATER - var runningOnLinuxArm64 = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && RuntimeInformation.ProcessArchitecture == Architecture.Arm64; -#else - var runningOnLinuxArm64 = false; -#endif - if (arm64Build && !runningOnLinuxArm64) + if (arm64Build) { - _logger?.WriteLine("The docker CLI \"buildx\" command is used to build ARM64 images. This requires version 20 or later of the docker CLI."); - arguments.Append($"buildx build --platform linux/arm64 "); + arguments.Append($"buildx build --platform linux/arm64 --provenance=false "); } else { - arguments.Append($"build "); + arguments.Append($"buildx build --platform linux/amd64 --provenance=false "); } arguments.Append($" -f \"{dockerFile}\" -t {imageTag}"); diff --git a/src/Amazon.Common.DotNetCli.Tools/ExtensionMethods.cs b/src/Amazon.Common.DotNetCli.Tools/ExtensionMethods.cs index 0b642d1b..2e067b31 100644 --- a/src/Amazon.Common.DotNetCli.Tools/ExtensionMethods.cs +++ b/src/Amazon.Common.DotNetCli.Tools/ExtensionMethods.cs @@ -1,21 +1,21 @@ using System; -using System.IO; using System.Collections.Generic; +using System.IO; using System.Text; -using ThirdParty.Json.LitJson; +using System.Text.Json; namespace Amazon.Common.DotNetCli.Tools { public static class ExtensionMethods { - public static void SetIfNotNull(this JsonData data, string key, string value) + public static void SetIfNotNull(this Dictionary data, string key, string value) { if (value == null) return; data[key] = value; } - public static void SetFilePathIfNotNull(this JsonData data, string key, string filepath, string rootPath) + public static void SetFilePathIfNotNull(this Dictionary data, string key, string filepath, string rootPath) { if (string.IsNullOrEmpty(filepath)) return; @@ -30,19 +30,51 @@ public static void SetFilePathIfNotNull(this JsonData data, string key, string f data[key] = filepath; } - public static void SetIfNotNull(this JsonData data, string key, bool? value) + public static void SetIfNotNull(this Dictionary data, string key, bool? value) { if (!value.HasValue) return; data[key] = value.Value; } - public static void SetIfNotNull(this JsonData data, string key, int? value) + public static void SetIfNotNull(this Dictionary data, string key, int? value) { if (!value.HasValue) return; data[key] = value.Value; } + + public static object GetJsonValue(this JsonElement element) + { + switch (element.ValueKind) + { + case JsonValueKind.String: + return element.GetString(); + case JsonValueKind.Number: + if (element.TryGetInt32(out int intValue)) + return intValue; + return element.GetDouble(); + case JsonValueKind.True: + case JsonValueKind.False: + return element.GetBoolean(); + case JsonValueKind.Array: + var array = new List(); + foreach (JsonElement item in element.EnumerateArray()) + { + array.Add(GetJsonValue(item)); + } + return array; + case JsonValueKind.Object: + var obj = new Dictionary(); + foreach (JsonProperty prop in element.EnumerateObject()) + { + obj[prop.Name] = GetJsonValue(prop.Value); + } + return obj; + default: + return null; + } + } } } diff --git a/src/Amazon.Common.DotNetCli.Tools/RoleHelper.cs b/src/Amazon.Common.DotNetCli.Tools/RoleHelper.cs index 235f78af..80451290 100644 --- a/src/Amazon.Common.DotNetCli.Tools/RoleHelper.cs +++ b/src/Amazon.Common.DotNetCli.Tools/RoleHelper.cs @@ -31,10 +31,14 @@ public static string GenerateUniqueIAMRoleName(IAmazonIdentityManagementService var response = new ListRolesResponse(); do { - var roles = iamClient.ListRolesAsync(new ListRolesRequest { Marker = response.Marker }).Result.Roles; - roles.ForEach(x => existingRoleNames.Add(x.RoleName)); + var listResponse = iamClient.ListRolesAsync(new ListRolesRequest { Marker = response.Marker }).Result; + response = listResponse; + if (listResponse.Roles != null) + { + listResponse.Roles.ForEach(x => existingRoleNames.Add(x.RoleName)); + } - } while (response.IsTruncated); + } while (response.IsTruncated.GetValueOrDefault()); if (!existingRoleNames.Contains(baseName)) return baseName; @@ -120,11 +124,14 @@ public static string ExpandManagedPolicyName(IAmazonIdentityManagementService ia { var listRequest = new ListPoliciesRequest { Marker = listResponse.Marker, Scope = PolicyScopeType.All }; listResponse = await iamClient.ListPoliciesAsync(listRequest).ConfigureAwait(false); - var policy = listResponse.Policies.FirstOrDefault(x => string.Equals(managedPolicy, x.PolicyName)); - if (policy != null) - return policy.Arn; + if (listResponse.Policies != null) + { + var policy = listResponse.Policies.FirstOrDefault(x => string.Equals(managedPolicy, x.PolicyName)); + if (policy != null) + return policy.Arn; + } - } while (listResponse.IsTruncated); + } while (listResponse.IsTruncated.GetValueOrDefault()); return null; }); @@ -240,34 +247,40 @@ public static async Task> FindManagedPoliciesAsync(IAmazonI request.Marker = response?.Marker; response = await iamClient.ListPoliciesAsync(request).ConfigureAwait(false); - foreach (var policy in response.Policies) + if (response.Policies != null) { - if (policy.IsAttachable && - (promptInfo.KnownManagedPolicyDescription.ContainsKey(policy.PolicyName) || - (promptInfo.AWSManagedPolicyNamePrefix != null && policy.PolicyName.StartsWith(promptInfo.AWSManagedPolicyNamePrefix))) - ) + foreach (var policy in response.Policies) { - policies.Add(policy); - } + if (policy.IsAttachable.GetValueOrDefault() && + (promptInfo.KnownManagedPolicyDescription.ContainsKey(policy.PolicyName) || + (promptInfo.AWSManagedPolicyNamePrefix != null && policy.PolicyName.StartsWith(promptInfo.AWSManagedPolicyNamePrefix))) + ) + { + policies.Add(policy); + } - if (policies.Count == maxPolicies) - return policies; + if (policies.Count == maxPolicies) + return policies; + } } - } while (response.IsTruncated); + } while (response.IsTruncated.GetValueOrDefault()); response = await iamClient.ListPoliciesAsync(new ListPoliciesRequest { Scope = PolicyScopeType.Local }); - foreach (var policy in response.Policies) + if (response.Policies != null) { - if (policy.IsAttachable) - policies.Add(policy); + foreach (var policy in response.Policies) + { + if (policy.IsAttachable.GetValueOrDefault()) + policies.Add(policy); - if (policies.Count == maxPolicies) - return policies; + if (policies.Count == maxPolicies) + return policies; + } } @@ -287,19 +300,22 @@ public static async Task> FindExistingRolesAsync(IAmazonIdentityMana response = await iamClient.ListRolesAsync(request).ConfigureAwait(false); - foreach (var role in response.Roles) + if (response.Roles != null) { - if (AssumeRoleServicePrincipalSelector(role, assumeRolePrincpal)) + foreach (var role in response.Roles) { - roles.Add(role); - if (roles.Count == maxRoles) + if (AssumeRoleServicePrincipalSelector(role, assumeRolePrincpal)) { - break; + roles.Add(role); + if (roles.Count == maxRoles) + { + break; + } } } } - } while (response.IsTruncated && roles.Count < maxRoles); + } while (response.IsTruncated.GetValueOrDefault() && roles.Count < maxRoles); return roles; @@ -353,12 +369,15 @@ public static async Task> FindExistingInstanceProfilesAsy response = await iamClient.ListInstanceProfilesAsync(request).ConfigureAwait(false); - foreach (var profile in response.InstanceProfiles) + if (response.InstanceProfiles != null) { - profiles.Add(profile); + foreach (var profile in response.InstanceProfiles) + { + profiles.Add(profile); + } } - } while (response.IsTruncated && profiles.Count < maxRoles); + } while (response.IsTruncated.GetValueOrDefault() && profiles.Count < maxRoles); return profiles; } diff --git a/src/Amazon.Common.DotNetCli.Tools/Utilities.cs b/src/Amazon.Common.DotNetCli.Tools/Utilities.cs index bf18c501..b013ebaa 100644 --- a/src/Amazon.Common.DotNetCli.Tools/Utilities.cs +++ b/src/Amazon.Common.DotNetCli.Tools/Utilities.cs @@ -260,15 +260,17 @@ public static Dictionary LookupProjectProperties(string projectL else { // Multiple properties were requested, so we expect JSON output - using JsonDocument doc = JsonDocument.Parse(output); - JsonElement root = doc.RootElement; - JsonElement propertiesElement = root.GetProperty("Properties"); - - foreach (var property in propertyNames) + using (JsonDocument doc = JsonDocument.Parse(output)) { - if (propertiesElement.TryGetProperty(property, out JsonElement propertyValue)) + JsonElement root = doc.RootElement; + JsonElement propertiesElement = root.GetProperty("Properties"); + + foreach (var property in propertyNames) { - properties[property] = propertyValue.GetString(); + if (propertiesElement.TryGetProperty(property, out JsonElement propertyValue)) + { + properties[property] = propertyValue.GetString(); + } } } } @@ -739,10 +741,12 @@ private static async Task GetBucketRegionAsync(IAmazonS3 s3Client, strin var request = new GetBucketLocationRequest { BucketName = bucket }; var response = await s3Client.GetBucketLocationAsync(request); - // Handle the legacy naming conventions - if (response.Location == S3Region.US) + // These 2 regions are special case because S3 has legacy behavior where the value of the enum + // doesn't match the region name. USEast1 returns back an empty string for a value and EUWest1 + // returns back "EU". + if (response.Location == S3Region.USEast1) return "us-east-1"; - if (response.Location == S3Region.EU) + if (response.Location == S3Region.EUWest1) return "eu-west-1"; return response.Location.Value; diff --git a/src/Amazon.ECS.Tools/Amazon.ECS.Tools.csproj b/src/Amazon.ECS.Tools/Amazon.ECS.Tools.csproj index ae40c654..2ff40435 100644 --- a/src/Amazon.ECS.Tools/Amazon.ECS.Tools.csproj +++ b/src/Amazon.ECS.Tools/Amazon.ECS.Tools.csproj @@ -4,7 +4,7 @@ Exe - netcoreapp3.1;net6.0 + net6.0;net8.0 Amazon.ECS.Tools AWS;Amazon;Docker;ECS true @@ -17,16 +17,21 @@ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. Amazon ECS Tools for .NET CLI Amazon.ECS.Tools adds commands to the dotnet cli to deploy .NET Core applications to Amazon Elastic Container Service. For example to deploy an ASP.NET Core application from the command execute "dotnet ecs deploy-service" on the command line. + + 7.3 - - - - - - - + + + + + + + @@ -36,7 +41,8 @@ - 1701;1702;1705;1591 + true + 1701;1702;1705;1591;NETSDK1138 diff --git a/src/Amazon.ECS.Tools/Commands/CommandProperties.cs b/src/Amazon.ECS.Tools/Commands/CommandProperties.cs index aed53d1a..b70d466f 100644 --- a/src/Amazon.ECS.Tools/Commands/CommandProperties.cs +++ b/src/Amazon.ECS.Tools/Commands/CommandProperties.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using ThirdParty.Json.LitJson; namespace Amazon.ECS.Tools.Commands { @@ -37,7 +36,7 @@ internal void ParseCommandArguments(CommandOptions values) } - internal void PersistSettings(ECSBaseCommand command, JsonData data) + internal void PersistSettings(ECSBaseCommand command, Dictionary data) { data.SetIfNotNull(CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION.ConfigFileKey, command.GetStringValueOrDefault(this.Configuration, CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION, false)); data.SetIfNotNull(CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK.ConfigFileKey, command.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, false)); @@ -92,7 +91,7 @@ internal void ParseCommandArguments(CommandOptions values) this.AssignPublicIpAddress = tuple.Item2.BoolValue; } - internal void PersistSettings(ECSBaseCommand command, JsonData data) + internal void PersistSettings(ECSBaseCommand command, Dictionary data) { data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_ECS_CLUSTER.ConfigFileKey, command.GetStringValueOrDefault(this.ECSCluster, ECSDefinedCommandOptions.ARGUMENT_ECS_CLUSTER, false)); data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_LAUNCH_TYPE.ConfigFileKey, command.GetStringValueOrDefault(this.LaunchType, ECSDefinedCommandOptions.ARGUMENT_LAUNCH_TYPE, false)); @@ -253,7 +252,7 @@ internal void ParseCommandArguments(CommandOptions values) this.TaskDefinitionVolumes = tuple.Item2.StringValue; } - internal void PersistSettings(ECSBaseCommand command, JsonData data) + internal void PersistSettings(ECSBaseCommand command, Dictionary data) { data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_TD_NAME.ConfigFileKey, command.GetStringValueOrDefault(this.TaskDefinitionName, ECSDefinedCommandOptions.ARGUMENT_TD_NAME, false)); data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_TD_NETWORK_MODE.ConfigFileKey, command.GetStringValueOrDefault(this.TaskDefinitionNetworkMode, ECSDefinedCommandOptions.ARGUMENT_TD_NETWORK_MODE, false)); @@ -340,7 +339,7 @@ internal void ParseCommandArguments(CommandOptions values) this.PlacementStrategy = tuple.Item2.StringValues; } - internal void PersistSettings(ECSBaseCommand command, JsonData data) + internal void PersistSettings(ECSBaseCommand command, Dictionary data) { data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_SKIP_IMAGE_PUSH.ConfigFileKey, command.GetBoolValueOrDefault(this.SkipImagePush, ECSDefinedCommandOptions.ARGUMENT_SKIP_IMAGE_PUSH, false)); data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_ECS_SERVICE.ConfigFileKey, command.GetStringValueOrDefault(this.ECSService, ECSDefinedCommandOptions.ARGUMENT_ECS_SERVICE, false)); @@ -377,7 +376,7 @@ internal void ParseCommandArguments(CommandOptions values) this.PlacementStrategy = tuple.Item2.StringValues; } - internal void PersistSettings(ECSBaseCommand command, JsonData data) + internal void PersistSettings(ECSBaseCommand command, Dictionary data) { data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_ECS_TASK_GROUP.ConfigFileKey, command.GetStringValueOrDefault(this.TaskGroup, ECSDefinedCommandOptions.ARGUMENT_ECS_TASK_GROUP, false)); data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_ECS_TASK_COUNT.ConfigFileKey, command.GetIntValueOrDefault(this.TaskCount, ECSDefinedCommandOptions.ARGUMENT_ECS_TASK_COUNT, false)); @@ -412,7 +411,7 @@ internal void ParseCommandArguments(CommandOptions values) this.DesiredCount = tuple.Item2.IntValue; } - internal void PersistSettings(ECSBaseCommand command, JsonData data) + internal void PersistSettings(ECSBaseCommand command, Dictionary data) { data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_SCHEDULED_RULE_NAME.ConfigFileKey, command.GetStringValueOrDefault(this.ScheduleTaskRule, ECSDefinedCommandOptions.ARGUMENT_SCHEDULED_RULE_NAME, false)); data.SetIfNotNull(ECSDefinedCommandOptions.ARGUMENT_SCHEDULED_RULE_TARGET.ConfigFileKey, command.GetStringValueOrDefault(this.ScheduleTaskRuleTarget, ECSDefinedCommandOptions.ARGUMENT_SCHEDULED_RULE_TARGET, false)); diff --git a/src/Amazon.ECS.Tools/Commands/DeployScheduledTaskCommand.cs b/src/Amazon.ECS.Tools/Commands/DeployScheduledTaskCommand.cs index 6691b363..34a04b19 100644 --- a/src/Amazon.ECS.Tools/Commands/DeployScheduledTaskCommand.cs +++ b/src/Amazon.ECS.Tools/Commands/DeployScheduledTaskCommand.cs @@ -9,7 +9,6 @@ using Amazon.ECR.Model; using Amazon.ECS; using Amazon.ECS.Model; -using ThirdParty.Json.LitJson; using System.IO; using Amazon.Common.DotNetCli.Tools.Options; using Amazon.Common.DotNetCli.Tools; @@ -207,7 +206,7 @@ await this.CWEClient.PutTargetsAsync(new PutTargetsRequest return true; } - protected override void SaveConfigFile(JsonData data) + protected override void SaveConfigFile(Dictionary data) { this.PushDockerImageProperties.PersistSettings(this, data); this.TaskDefinitionProperties.PersistSettings(this, data); diff --git a/src/Amazon.ECS.Tools/Commands/DeployServiceCommand.cs b/src/Amazon.ECS.Tools/Commands/DeployServiceCommand.cs index 5a3ef23b..ac7a4734 100644 --- a/src/Amazon.ECS.Tools/Commands/DeployServiceCommand.cs +++ b/src/Amazon.ECS.Tools/Commands/DeployServiceCommand.cs @@ -8,7 +8,6 @@ using Amazon.ECR.Model; using Amazon.ECS.Model; -using ThirdParty.Json.LitJson; using System.IO; using Amazon.Common.DotNetCli.Tools.Options; using Amazon.Common.DotNetCli.Tools; @@ -176,7 +175,7 @@ private async Task CreateOrUpdateService(string ecsCluster, string ecsService, s NetworkConfiguration networkConfiguration = null; if (IsFargateLaunch(this.ClusterProperties.LaunchType)) { - if (describeServiceResponse.Services.Count != 0) + if (describeServiceResponse.Services != null && describeServiceResponse.Services.Count != 0) networkConfiguration = describeServiceResponse.Services[0].NetworkConfiguration; else { @@ -196,7 +195,7 @@ private async Task CreateOrUpdateService(string ecsCluster, string ecsService, s deploymentConfiguration.MinimumHealthyPercent = deploymentMinimumHealthyPercent.Value; } - if (describeServiceResponse.Services.Count == 0 || describeServiceResponse.Services[0].Status == "INACTIVE") + if (describeServiceResponse.Services == null || describeServiceResponse.Services.Count == 0 || describeServiceResponse.Services[0].Status == "INACTIVE") { this.Logger?.WriteLine($"Creating new service: {ecsService}"); var request = new CreateServiceRequest @@ -232,6 +231,8 @@ private async Task CreateOrUpdateService(string ecsCluster, string ecsService, s var port = this.GetIntValueOrDefault(this.DeployServiceProperties.ELBContainerPort, ECSDefinedCommandOptions.ARGUMENT_ELB_CONTAINER_PORT, false); if (!port.HasValue) port = 80; + if (request.LoadBalancers == null) + request.LoadBalancers = new List(); request.LoadBalancers.Add(new LoadBalancer { TargetGroupArn = elbTargetGroup, @@ -248,7 +249,8 @@ private async Task CreateOrUpdateService(string ecsCluster, string ecsService, s { if (e.Message.StartsWith("The target group") && !string.IsNullOrEmpty(elbTargetGroup) && string.IsNullOrEmpty(this.DeployServiceProperties.ELBTargetGroup)) { - request.LoadBalancers.Clear(); + if (request.LoadBalancers != null) + request.LoadBalancers.Clear(); request.Role = null; var defaultFile = string.IsNullOrEmpty(this.ConfigFile) ? ECSToolsDefaults.DEFAULT_FILE_NAME : this.ConfigFile; @@ -291,7 +293,7 @@ private async Task CreateOrUpdateService(string ecsCluster, string ecsService, s } } - protected override void SaveConfigFile(JsonData data) + protected override void SaveConfigFile(Dictionary data) { PushDockerImageProperties.PersistSettings(this, data); ClusterProperties.PersistSettings(this, data); diff --git a/src/Amazon.ECS.Tools/Commands/DeployTaskCommand.cs b/src/Amazon.ECS.Tools/Commands/DeployTaskCommand.cs index 96d4f3ae..19851f02 100644 --- a/src/Amazon.ECS.Tools/Commands/DeployTaskCommand.cs +++ b/src/Amazon.ECS.Tools/Commands/DeployTaskCommand.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using ThirdParty.Json.LitJson; namespace Amazon.ECS.Tools.Commands { @@ -165,10 +164,17 @@ protected override async Task PerformActionAsync() try { var response = await this.ECSClient.RunTaskAsync(runTaskRequest); - this.Logger?.WriteLine($"Started {response.Tasks.Count} task:"); - foreach(var task in response.Tasks) + if (response.Tasks != null) { - this.Logger?.WriteLine($"\t{task.TaskArn}"); + this.Logger?.WriteLine($"Started {response.Tasks.Count} task:"); + foreach(var task in response.Tasks) + { + this.Logger?.WriteLine($"\t{task.TaskArn}"); + } + } + else + { + this.Logger?.WriteLine("No tasks were started"); } } catch(Exception e) @@ -184,7 +190,7 @@ protected override async Task PerformActionAsync() return true; } - protected override void SaveConfigFile(JsonData data) + protected override void SaveConfigFile(Dictionary data) { this.PushDockerImageProperties.PersistSettings(this, data); this.TaskDefinitionProperties.PersistSettings(this, data); diff --git a/src/Amazon.ECS.Tools/Commands/ECSBaseCommand.cs b/src/Amazon.ECS.Tools/Commands/ECSBaseCommand.cs index 4cc3a374..8abf476a 100644 --- a/src/Amazon.ECS.Tools/Commands/ECSBaseCommand.cs +++ b/src/Amazon.ECS.Tools/Commands/ECSBaseCommand.cs @@ -14,7 +14,6 @@ using Amazon.ECS; using System.Reflection; -using ThirdParty.Json.LitJson; using System.Text; using System.IO; using Amazon.Common.DotNetCli.Tools.Commands; diff --git a/src/Amazon.ECS.Tools/Commands/PushDockerImageCommand.cs b/src/Amazon.ECS.Tools/Commands/PushDockerImageCommand.cs index 52f9782f..1cf6829d 100644 --- a/src/Amazon.ECS.Tools/Commands/PushDockerImageCommand.cs +++ b/src/Amazon.ECS.Tools/Commands/PushDockerImageCommand.cs @@ -5,7 +5,6 @@ using Amazon.ECR.Model; using Amazon.ECR; -using ThirdParty.Json.LitJson; using System.IO; using Amazon.Common.DotNetCli.Tools.Options; using Amazon.Common.DotNetCli.Tools; diff --git a/src/Amazon.ECS.Tools/ECSToolsDefaults.cs b/src/Amazon.ECS.Tools/ECSToolsDefaults.cs index fcbe96b6..f65955ac 100644 --- a/src/Amazon.ECS.Tools/ECSToolsDefaults.cs +++ b/src/Amazon.ECS.Tools/ECSToolsDefaults.cs @@ -7,8 +7,8 @@ using Amazon.ECS.Tools.Commands; -using ThirdParty.Json.LitJson; using Amazon.Common.DotNetCli.Tools; +using System.Text.Json; namespace Amazon.ECS.Tools { @@ -25,11 +25,11 @@ public ECSToolsDefaults() } public ECSToolsDefaults(string sourceFile) - : this(new JsonData(), sourceFile) + : this(new JsonElement(), sourceFile) { } - public ECSToolsDefaults(JsonData data, string sourceFile) + public ECSToolsDefaults(JsonElement data, string sourceFile) : base(data, sourceFile) { } diff --git a/src/Amazon.ECS.Tools/ECSUtilities.cs b/src/Amazon.ECS.Tools/ECSUtilities.cs index e05d33dc..4b34b342 100644 --- a/src/Amazon.ECS.Tools/ECSUtilities.cs +++ b/src/Amazon.ECS.Tools/ECSUtilities.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Text.Json; using System.Threading.Tasks; using Amazon.EC2.Model; using Amazon.ECS.Model; @@ -12,7 +13,6 @@ using Amazon.ECS.Tools.Commands; using Amazon.CloudWatchLogs.Model; using Amazon.CloudWatchLogs; -using ThirdParty.Json.LitJson; using System.Linq; using Task = System.Threading.Tasks.Task; @@ -96,7 +96,7 @@ public static async Task ExpandImageTagIfNecessary(IToolLogger logger, I } // Not found in ECR, assume pulling Docker Hub - if (describeResponse == null) + if (describeResponse == null || describeResponse.Repositories == null || describeResponse.Repositories.Count == 0) { return dockerImageTag; } @@ -121,7 +121,7 @@ public static async System.Threading.Tasks.Task EnsureClusterExistsAsync(IToolLo { logger.WriteLine($"Checking to see if cluster {clusterName} exists"); var response = await ecsClient.DescribeClustersAsync(new DescribeClustersRequest { Clusters = new List { clusterName } }); - if (response.Clusters.Count == 0 || string.Equals(response.Clusters[0].Status, "INACTIVE", StringComparison.OrdinalIgnoreCase)) + if (response.Clusters == null || response.Clusters.Count == 0 || string.Equals(response.Clusters[0].Status, "INACTIVE", StringComparison.OrdinalIgnoreCase)) { logger.WriteLine($"... Cluster does not exist, creating cluster {clusterName}"); await ecsClient.CreateClusterAsync(new CreateClusterRequest { ClusterName = clusterName }); @@ -192,47 +192,52 @@ public static async Task CreateOrUpdateTaskDefinition(IToolLogger logger } { - JsonData volumes = command.GetJsonValueOrDefault(properties.TaskDefinitionVolumes, ECSDefinedCommandOptions.ARGUMENT_TD_VOLUMES); - if (volumes != null) + var volumes = command.GetJsonValueOrDefault(properties.TaskDefinitionVolumes, ECSDefinedCommandOptions.ARGUMENT_TD_VOLUMES); + if (volumes.HasValue) { registerRequest.Volumes = new List(); - foreach (JsonData item in volumes) + foreach (JsonElement item in volumes.Value.EnumerateArray()) { var volume = new Amazon.ECS.Model.Volume(); - if (item["host"] != null) + if (item.TryGetProperty("host", out JsonElement host)) { volume.Host = new HostVolumeProperties(); - volume.Host.SourcePath = item["host"]["sourcePath"] != null ? item["host"]["sourcePath"].ToString() : null; + if (host.TryGetProperty("sourcePath", out JsonElement sourcePath)) + volume.Host.SourcePath = sourcePath.GetString(); } - if (item["efsVolumeConfiguration"] != null) + if (item.TryGetProperty("efsVolumeConfiguration", out JsonElement efsConfig)) { volume.EfsVolumeConfiguration = new EFSVolumeConfiguration(); - volume.EfsVolumeConfiguration.FileSystemId = item["efsVolumeConfiguration"]["fileSystemId"]?.ToString(); - volume.EfsVolumeConfiguration.RootDirectory = item["efsVolumeConfiguration"]["rootDirectory"]?.ToString(); - var transitEncryption = item["efsVolumeConfiguration"]["transitEncryption"]?.ToString(); - if (transitEncryption != null) + if (efsConfig.TryGetProperty("fileSystemId", out JsonElement fileSystemId)) + volume.EfsVolumeConfiguration.FileSystemId = fileSystemId.GetString(); + if (efsConfig.TryGetProperty("rootDirectory", out JsonElement rootDirectory)) + volume.EfsVolumeConfiguration.RootDirectory = rootDirectory.GetString(); + if (efsConfig.TryGetProperty("transitEncryption", out JsonElement transitEncryption)) { - volume.EfsVolumeConfiguration.TransitEncryption = EFSTransitEncryption.FindValue(transitEncryption); + var transitEncryptionValue = transitEncryption.GetString(); + if (transitEncryptionValue != null) + volume.EfsVolumeConfiguration.TransitEncryption = EFSTransitEncryption.FindValue(transitEncryptionValue); } - var transitEncryptionPort = item["efsVolumeConfiguration"]["transitEncryptionPort"]; - if (transitEncryptionPort != null && transitEncryptionPort.IsInt) + if (efsConfig.TryGetProperty("transitEncryptionPort", out JsonElement transitEncryptionPort) && transitEncryptionPort.ValueKind == JsonValueKind.Number) { - volume.EfsVolumeConfiguration.TransitEncryptionPort = (int)transitEncryptionPort; + volume.EfsVolumeConfiguration.TransitEncryptionPort = transitEncryptionPort.GetInt32(); } - var authorizationConfig = item["efsVolumeConfiguration"]["authorizationConfig"]; - if (authorizationConfig != null) + if (efsConfig.TryGetProperty("authorizationConfig", out JsonElement authConfig)) { volume.EfsVolumeConfiguration.AuthorizationConfig = new EFSAuthorizationConfig(); - volume.EfsVolumeConfiguration.AuthorizationConfig.AccessPointId = authorizationConfig["accessPointId"]?.ToString(); - var iam = authorizationConfig["iam"]?.ToString(); - if (iam != null) + if (authConfig.TryGetProperty("accessPointId", out JsonElement accessPointId)) + volume.EfsVolumeConfiguration.AuthorizationConfig.AccessPointId = accessPointId.GetString(); + if (authConfig.TryGetProperty("iam", out JsonElement iam)) { - volume.EfsVolumeConfiguration.AuthorizationConfig.Iam = EFSAuthorizationConfigIAM.FindValue(iam); + var iamValue = iam.GetString(); + if (iamValue != null) + volume.EfsVolumeConfiguration.AuthorizationConfig.Iam = EFSAuthorizationConfigIAM.FindValue(iamValue); } } } - volume.Name = item["name"] != null ? item["name"].ToString() : null; + if (item.TryGetProperty("name", out JsonElement name)) + volume.Name = name.GetString(); registerRequest.Volumes.Add(volume); } @@ -245,6 +250,11 @@ public static async Task CreateOrUpdateTaskDefinition(IToolLogger logger registerRequest.TaskRoleArn = taskIAMRole; } + if (registerRequest.ContainerDefinitions == null) + { + registerRequest.ContainerDefinitions = new List(); + } + var containerDefinition = registerRequest.ContainerDefinitions.FirstOrDefault(x => string.Equals(x.Name, ecsContainer, StringComparison.Ordinal)); if (containerDefinition == null) @@ -329,16 +339,20 @@ public static async Task CreateOrUpdateTaskDefinition(IToolLogger logger } } { - JsonData data = command.GetJsonValueOrDefault(properties.ContainerExtraHosts, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_EXTRA_HOSTS); - if (data != null) + var data = command.GetJsonValueOrDefault(properties.ContainerExtraHosts, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_EXTRA_HOSTS); + if (data.HasValue) { + if (containerDefinition.ExtraHosts == null) + containerDefinition.ExtraHosts = new List(); containerDefinition.ExtraHosts.Clear(); - foreach (JsonData item in data) + foreach (JsonElement item in data.Value.EnumerateArray()) { var obj = new Amazon.ECS.Model.HostEntry(); - obj.Hostname = item["hostname"] != null ? item["hostname"].ToString() : null; - obj.IpAddress = item["ipAddress"] != null ? item["ipAddress"].ToString() : null; + if (item.TryGetProperty("hostname", out JsonElement hostname)) + obj.Hostname = hostname.GetString(); + if (item.TryGetProperty("ipAddress", out JsonElement ipAddress)) + obj.IpAddress = ipAddress.GetString(); containerDefinition.ExtraHosts.Add(obj); } } @@ -358,74 +372,89 @@ public static async Task CreateOrUpdateTaskDefinition(IToolLogger logger } } { - JsonData data = command.GetJsonValueOrDefault(properties.ContainerExtraHosts, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_EXTRA_HOSTS); - if (data != null) + var data = command.GetJsonValueOrDefault(properties.ContainerLinuxParameters, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_LINUX_PARAMETERS); + if (data.HasValue) { var linuxParameter = new LinuxParameters(); - if (data["capabilities"] != null) + if (data.Value.TryGetProperty("capabilities", out JsonElement capabilities)) { linuxParameter.Capabilities = new KernelCapabilities(); - if (data["capabilities"]["drop"] != null) + if (capabilities.TryGetProperty("drop", out JsonElement drop)) { - foreach (var item in data["capabilities"]["drop"]) + if (linuxParameter.Capabilities.Drop == null) + linuxParameter.Capabilities.Drop = new List(); + foreach (JsonElement item in drop.EnumerateArray()) { - linuxParameter.Capabilities.Drop.Add(item.ToString()); + linuxParameter.Capabilities.Drop.Add(item.GetString()); } } - if (data["capabilities"]["add"] != null) + if (capabilities.TryGetProperty("add", out JsonElement add)) { - foreach (var item in data["capabilities"]["add"]) + if (linuxParameter.Capabilities.Add == null) + linuxParameter.Capabilities.Add = new List(); + foreach (JsonElement item in add.EnumerateArray()) { - linuxParameter.Capabilities.Add.Add(item.ToString()); + linuxParameter.Capabilities.Add.Add(item.GetString()); } } } - if (data["devices"] != null) + if (data.Value.TryGetProperty("devices", out JsonElement devices)) { + if (linuxParameter.Devices == null) + linuxParameter.Devices = new List(); linuxParameter.Devices.Clear(); - foreach (JsonData item in data["devices"]) + foreach (JsonElement item in devices.EnumerateArray()) { var device = new Device(); - device.ContainerPath = item["containerPath"] != null ? item["containerPath"].ToString() : null; - device.HostPath = item["hostPath"] != null ? item["hostPath"].ToString() : null; - foreach (string permission in item["permissions"]) + if (item.TryGetProperty("containerPath", out JsonElement containerPath)) + device.ContainerPath = containerPath.GetString(); + if (item.TryGetProperty("hostPath", out JsonElement hostPath)) + device.HostPath = hostPath.GetString(); + if (item.TryGetProperty("permissions", out JsonElement permissions)) { - device.Permissions.Add(permission); + if (device.Permissions == null) + device.Permissions = new List(); + foreach (JsonElement permission in permissions.EnumerateArray()) + { + device.Permissions.Add(permission.GetString()); + } } linuxParameter.Devices.Add(device); } } - if (data["initProcessEnabled"] != null && data["initProcessEnabled"].IsBoolean) - linuxParameter.InitProcessEnabled = (bool)data["initProcessEnabled"]; + if (data.Value.TryGetProperty("initProcessEnabled", out JsonElement initProcessEnabled) && initProcessEnabled.ValueKind == JsonValueKind.True) + linuxParameter.InitProcessEnabled = initProcessEnabled.GetBoolean(); containerDefinition.LinuxParameters = linuxParameter; } } { - JsonData data = command.GetJsonValueOrDefault(properties.ContainerLogConfiguration, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_LOG_CONFIGURATION); - if (data != null) + var data = command.GetJsonValueOrDefault(properties.ContainerLogConfiguration, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_LOG_CONFIGURATION); + if (data.HasValue) { containerDefinition.LogConfiguration = new LogConfiguration(); containerDefinition.LogConfiguration.LogDriver = null; // Added support for logDriver JSON key as fix for legacy bug where JSON key containerPath was used. - if (data["logDriver"] != null) + if (data.Value.TryGetProperty("logDriver", out JsonElement logDriver)) { - containerDefinition.LogConfiguration.LogDriver = data["logDriver"].ToString(); + containerDefinition.LogConfiguration.LogDriver = logDriver.GetString(); } - else if (data["containerPath"] != null) // Retained legacy containerPath JSON key support. Removing this would break existing customers. + else if (data.Value.TryGetProperty("containerPath", out JsonElement containerPath)) // Retained legacy containerPath JSON key support. Removing this would break existing customers. { - containerDefinition.LogConfiguration.LogDriver = data["containerPath"].ToString(); + containerDefinition.LogConfiguration.LogDriver = containerPath.GetString(); } - if (data["options"] != null) + if (data.Value.TryGetProperty("options", out JsonElement options)) { - foreach (var key in data["options"].PropertyNames) + if (containerDefinition.LogConfiguration.Options == null) + containerDefinition.LogConfiguration.Options = new Dictionary(); + foreach (JsonProperty prop in options.EnumerateObject()) { - containerDefinition.LogConfiguration.Options[key] = data["options"][key].ToString(); + containerDefinition.LogConfiguration.Options[prop.Name] = prop.Value.GetString(); } } } @@ -447,18 +476,22 @@ public static async Task CreateOrUpdateTaskDefinition(IToolLogger logger } } { - JsonData data = command.GetJsonValueOrDefault(properties.ContainerMountPoints, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_MOUNT_POINTS); - if (data != null) + var data = command.GetJsonValueOrDefault(properties.ContainerMountPoints, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_MOUNT_POINTS); + if (data.HasValue) { + if (containerDefinition.MountPoints == null) + containerDefinition.MountPoints = new List(); containerDefinition.MountPoints.Clear(); - foreach (JsonData item in data) + foreach (JsonElement item in data.Value.EnumerateArray()) { var mountPoint = new MountPoint(); - mountPoint.ContainerPath = item["containerPath"] != null ? item["containerPath"].ToString() : null; - mountPoint.SourceVolume = item["sourceVolume"] != null ? item["sourceVolume"].ToString() : null; - if (item["readOnly"] != null && item["readOnly"].IsBoolean) + if (item.TryGetProperty("containerPath", out JsonElement containerPath)) + mountPoint.ContainerPath = containerPath.GetString(); + if (item.TryGetProperty("sourceVolume", out JsonElement sourceVolume)) + mountPoint.SourceVolume = sourceVolume.GetString(); + if (item.TryGetProperty("readOnly", out JsonElement readOnly) && (readOnly.ValueKind == JsonValueKind.True || readOnly.ValueKind == JsonValueKind.False)) { - mountPoint.ReadOnly = (bool)item["readOnly"]; + mountPoint.ReadOnly = readOnly.GetBoolean(); } containerDefinition.MountPoints.Add(mountPoint); @@ -503,22 +536,25 @@ public static async Task CreateOrUpdateTaskDefinition(IToolLogger logger } } { - JsonData data = command.GetJsonValueOrDefault(properties.ContainerUlimits, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_ULIMITS); - if (data != null) + var data = command.GetJsonValueOrDefault(properties.ContainerUlimits, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_ULIMITS); + if (data.HasValue) { + if (containerDefinition.Ulimits == null) + containerDefinition.Ulimits = new List(); containerDefinition.Ulimits.Clear(); - foreach (JsonData item in data) + foreach (JsonElement item in data.Value.EnumerateArray()) { var ulimit = new Ulimit(); - ulimit.Name = item["name"] != null ? item["name"].ToString() : null; - if (item["hardLimit"] != null && item["hardLimit"].IsInt) + if (item.TryGetProperty("name", out JsonElement name)) + ulimit.Name = name.GetString(); + if (item.TryGetProperty("hardLimit", out JsonElement hardLimit) && hardLimit.ValueKind == JsonValueKind.Number) { - ulimit.HardLimit = (int)item["hardLimit"]; + ulimit.HardLimit = hardLimit.GetInt32(); } - if (item["softLimit"] != null && item["softLimit"].IsInt) + if (item.TryGetProperty("softLimit", out JsonElement softLimit) && softLimit.ValueKind == JsonValueKind.Number) { - ulimit.HardLimit = (int)item["softLimit"]; + ulimit.SoftLimit = softLimit.GetInt32(); } containerDefinition.Ulimits.Add(ulimit); @@ -533,17 +569,20 @@ public static async Task CreateOrUpdateTaskDefinition(IToolLogger logger } } { - JsonData data = command.GetJsonValueOrDefault(properties.ContainerVolumesFrom, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_VOLUMES_FROM); - if (data != null) + var data = command.GetJsonValueOrDefault(properties.ContainerVolumesFrom, ECSDefinedCommandOptions.ARGUMENT_CONTAINER_VOLUMES_FROM); + if (data.HasValue) { + if (containerDefinition.VolumesFrom == null) + containerDefinition.VolumesFrom = new List(); containerDefinition.VolumesFrom.Clear(); - foreach (JsonData item in data) + foreach (JsonElement item in data.Value.EnumerateArray()) { var volume = new VolumeFrom(); - volume.SourceContainer = item["sourceContainer"] != null ? item["sourceContainer"].ToString() : null; - if (item["readOnly"] != null && item["readOnly"].IsBoolean) + if (item.TryGetProperty("sourceContainer", out JsonElement sourceContainer)) + volume.SourceContainer = sourceContainer.GetString(); + if (item.TryGetProperty("readOnly", out JsonElement readOnly) && (readOnly.ValueKind == JsonValueKind.True || readOnly.ValueKind == JsonValueKind.False)) { - volume.ReadOnly = (bool)item["readOnly"]; + volume.ReadOnly = readOnly.GetBoolean(); } containerDefinition.VolumesFrom.Add(volume); @@ -597,7 +636,8 @@ public static async Task CreateOrUpdateTaskDefinition(IToolLogger logger if (registerRequest.NetworkMode != NetworkMode.Awsvpc) { - registerRequest.RequiresCompatibilities.Clear(); + if (registerRequest.RequiresCompatibilities != null) + registerRequest.RequiresCompatibilities.Clear(); } if (containerDefinition.LogConfiguration == null || containerDefinition.LogConfiguration.LogDriver == LogDriver.Awslogs) @@ -643,7 +683,7 @@ public static async System.Threading.Tasks.Task EnsureLogGroupExistsAsync(IToolL LogGroupNamePrefix = logGroup }); - if (response.LogGroups.FirstOrDefault(x => string.Equals(logGroup, x.LogGroupName, StringComparison.Ordinal)) != null) + if (response.LogGroups != null && response.LogGroups.FirstOrDefault(x => string.Equals(logGroup, x.LogGroupName, StringComparison.Ordinal)) != null) { logger?.WriteLine("Found existing log group " + logGroup + " for container"); return; @@ -704,7 +744,7 @@ public static async System.Threading.Tasks.Task SetupAwsVpcNetworkConfigurationA string defaultVpcId = null; - bool noExistingSubnets = networkConfiguration.AwsvpcConfiguration.Subnets.Count == 0; + bool noExistingSubnets = networkConfiguration.AwsvpcConfiguration.Subnets == null || networkConfiguration.AwsvpcConfiguration.Subnets.Count == 0; var vpcSubnetWrapper = await SetupAwsVpcNetworkConfigurationSubnets(command, defaultVpcId, noExistingSubnets); var subnets = vpcSubnetWrapper.Subnets; @@ -715,7 +755,7 @@ public static async System.Threading.Tasks.Task SetupAwsVpcNetworkConfigurationA networkConfiguration.AwsvpcConfiguration.Subnets = new List(subnets); } - bool noExistingSecurityGroups = networkConfiguration.AwsvpcConfiguration.SecurityGroups.Count == 0; + bool noExistingSecurityGroups = networkConfiguration.AwsvpcConfiguration.SecurityGroups == null || networkConfiguration.AwsvpcConfiguration.SecurityGroups.Count == 0; var securityGroups = await SetupAwsVpcNetworkConfigurationSecurityGroups(command, defaultVpcId, noExistingSecurityGroups); if (securityGroups != null) @@ -745,7 +785,7 @@ public static async System.Threading.Tasks.Task SetupAwsVpcNetworkConfigurationC string defaultVpcId = null; - bool noExistingSubnets = networkConfiguration.AwsvpcConfiguration.Subnets.Count == 0; + bool noExistingSubnets = networkConfiguration.AwsvpcConfiguration.Subnets == null || networkConfiguration.AwsvpcConfiguration.Subnets.Count == 0; var vpcSubnetWrapper = await SetupAwsVpcNetworkConfigurationSubnets(command, defaultVpcId, noExistingSubnets); var subnets = vpcSubnetWrapper.Subnets; defaultVpcId = vpcSubnetWrapper.VpcId; @@ -755,7 +795,7 @@ public static async System.Threading.Tasks.Task SetupAwsVpcNetworkConfigurationC networkConfiguration.AwsvpcConfiguration.Subnets = new List(subnets); } - bool noExistingSecurityGroups = networkConfiguration.AwsvpcConfiguration.SecurityGroups.Count == 0; + bool noExistingSecurityGroups = networkConfiguration.AwsvpcConfiguration.SecurityGroups == null || networkConfiguration.AwsvpcConfiguration.SecurityGroups.Count == 0; var securityGroups = await SetupAwsVpcNetworkConfigurationSecurityGroups(command, defaultVpcId, noExistingSecurityGroups); if (securityGroups != null) @@ -788,18 +828,21 @@ private static async Task SetupAwsVpcNetworkConfigurationSubne try { var describeSubnetResponse = await command.EC2Client.DescribeSubnetsAsync(); - foreach (var subnet in describeSubnetResponse.Subnets) + if (describeSubnetResponse.Subnets != null) { - if (subnet.DefaultForAz) + foreach (var subnet in describeSubnetResponse.Subnets) { - if (defaultVpcId == null) + if (subnet.DefaultForAz.GetValueOrDefault()) { - command.Logger?.WriteLine("Default VPC: " + subnet.VpcId); - defaultVpcId = subnet.VpcId; - } + if (defaultVpcId == null) + { + command.Logger?.WriteLine("Default VPC: " + subnet.VpcId); + defaultVpcId = subnet.VpcId; + } - command.Logger?.WriteLine($"... Using subnet {subnet.SubnetId} ({subnet.AvailabilityZone})"); - defaultSubnets.Add(subnet.SubnetId); + command.Logger?.WriteLine($"... Using subnet {subnet.SubnetId} ({subnet.AvailabilityZone})"); + defaultSubnets.Add(subnet.SubnetId); + } } } } @@ -834,7 +877,7 @@ private static async Task SetupAwsVpcNetworkConfigurationSecurityGroup try { var describeVpcResponse = await command.EC2Client.DescribeVpcsAsync(); - var defaultVpc = describeVpcResponse.Vpcs.FirstOrDefault(x => x.IsDefault); + var defaultVpc = describeVpcResponse.Vpcs?.FirstOrDefault(x => x.IsDefault.GetValueOrDefault()); if (defaultVpc != null) { command.Logger?.WriteLine("Default VPC: " + defaultVpc.VpcId); @@ -861,7 +904,7 @@ private static async Task SetupAwsVpcNetworkConfigurationSecurityGroup Filters = new List { new Filter { Name = "vpc-id", Values = new List { defaultVpcId } } } }); - var defaultSecurityGroup = describeSecurityGroupResponse.SecurityGroups.FirstOrDefault(x => string.Equals(x.GroupName, "default", StringComparison.OrdinalIgnoreCase)); + var defaultSecurityGroup = describeSecurityGroupResponse.SecurityGroups?.FirstOrDefault(x => string.Equals(x.GroupName, "default", StringComparison.OrdinalIgnoreCase)); if (defaultSecurityGroup != null) { diff --git a/src/Amazon.ElasticBeanstalk.Tools/Amazon.ElasticBeanstalk.Tools.csproj b/src/Amazon.ElasticBeanstalk.Tools/Amazon.ElasticBeanstalk.Tools.csproj index ad3e5cc2..37b86ce5 100644 --- a/src/Amazon.ElasticBeanstalk.Tools/Amazon.ElasticBeanstalk.Tools.csproj +++ b/src/Amazon.ElasticBeanstalk.Tools/Amazon.ElasticBeanstalk.Tools.csproj @@ -1,10 +1,10 @@ - + Exe - netcoreapp3.1;net6.0 + net6.0;net8.0 Amazon.ElasticBeanstalk.Tools AWS;Amazon;Beanstalk true @@ -16,6 +16,11 @@ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. AWS Elastic Beanstalk Tools for .NET CLI Amazon.ElasticBeanstalk.Tools adds commands to the dotnet cli to deploy ASP.NET Core applications to AWS Elastic Beanstalk. For example to deploy an ASP.NET Core application from the command execute "dotnet eb deploy-environment" on the command line. + + 7.3 @@ -25,13 +30,14 @@ - - - + + + - 1701;1702;1705;1591 + true + 1701;1702;1705;1591;NETSDK1138 diff --git a/src/Amazon.ElasticBeanstalk.Tools/Commands/CommandProperties.cs b/src/Amazon.ElasticBeanstalk.Tools/Commands/CommandProperties.cs index f30cb653..0b331136 100644 --- a/src/Amazon.ElasticBeanstalk.Tools/Commands/CommandProperties.cs +++ b/src/Amazon.ElasticBeanstalk.Tools/Commands/CommandProperties.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.Text; -using ThirdParty.Json.LitJson; namespace Amazon.ElasticBeanstalk.Tools.Commands { @@ -103,7 +102,7 @@ internal void ParseCommandArguments(CommandOptions values) } - internal void PersistSettings(EBBaseCommand command, JsonData data) + internal void PersistSettings(EBBaseCommand command, Dictionary data) { data.SetIfNotNull(CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION.ConfigFileKey, command.GetStringValueOrDefault(this.Configuration, CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION, false)); data.SetIfNotNull(CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK.ConfigFileKey, command.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, false)); @@ -147,7 +146,7 @@ internal void ParseCommandArguments(CommandOptions values) } - internal void PersistSettings(EBBaseCommand command, JsonData data) + internal void PersistSettings(EBBaseCommand command, Dictionary data) { data.SetIfNotNull(EBDefinedCommandOptions.ARGUMENT_EB_ENVIRONMENT.ConfigFileKey, command.GetStringValueOrDefault(this.Environment, EBDefinedCommandOptions.ARGUMENT_EB_ENVIRONMENT, false)); } diff --git a/src/Amazon.ElasticBeanstalk.Tools/Commands/DeleteEnvironmentCommand.cs b/src/Amazon.ElasticBeanstalk.Tools/Commands/DeleteEnvironmentCommand.cs index 758d70e3..569db4ca 100644 --- a/src/Amazon.ElasticBeanstalk.Tools/Commands/DeleteEnvironmentCommand.cs +++ b/src/Amazon.ElasticBeanstalk.Tools/Commands/DeleteEnvironmentCommand.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using ThirdParty.Json.LitJson; namespace Amazon.ElasticBeanstalk.Tools.Commands { @@ -80,7 +79,7 @@ await this.EBClient.TerminateEnvironmentAsync(new TerminateEnvironmentRequest return true; } - protected override void SaveConfigFile(JsonData data) + protected override void SaveConfigFile(Dictionary data) { this.DeleteEnvironmentProperties.PersistSettings(this, data); } diff --git a/src/Amazon.ElasticBeanstalk.Tools/Commands/DeployEnvironmentCommand.cs b/src/Amazon.ElasticBeanstalk.Tools/Commands/DeployEnvironmentCommand.cs index 9aa1bec3..34543b79 100644 --- a/src/Amazon.ElasticBeanstalk.Tools/Commands/DeployEnvironmentCommand.cs +++ b/src/Amazon.ElasticBeanstalk.Tools/Commands/DeployEnvironmentCommand.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using ThirdParty.Json.LitJson; namespace Amazon.ElasticBeanstalk.Tools.Commands { @@ -111,7 +110,7 @@ protected override async Task PerformActionAsync() EnvironmentName = environmentDescription.EnvironmentName }); - if(response.ConfigurationSettings.Count != 1) + if(response.ConfigurationSettings == null || response.ConfigurationSettings.Count != 1) { throw new ElasticBeanstalkExceptions($"Unknown error to retrieving settings for existing Beanstalk environment.", ElasticBeanstalkExceptions.EBCode.FailedToDescribeEnvironmentSettings); } @@ -330,6 +329,7 @@ private async Task CreateEnvironment(string application, string environm ApplicationName = application, EnvironmentName = environment, VersionLabel = versionLabel, + OptionSettings = new List(), SolutionStackName = this.GetSolutionStackOrDefault(this.DeployEnvironmentOptions.SolutionStack, EBDefinedCommandOptions.ARGUMENT_SOLUTION_STACK, true), CNAMEPrefix = this.GetStringValueOrDefault(this.DeployEnvironmentOptions.CNamePrefix, EBDefinedCommandOptions.ARGUMENT_CNAME_PREFIX, false) }; @@ -420,11 +420,13 @@ private async Task CreateEnvironment(string application, string environm }); } - AddAdditionalOptions(createRequest.OptionSettings, true, isWindowsEnvironment); + createRequest.OptionSettings = AddAdditionalOptions(createRequest.OptionSettings, true, isWindowsEnvironment); var tags = ConvertToTagsCollection(); if (tags != null && tags.Count > 0) + { createRequest.Tags = tags; + } try { @@ -437,8 +439,11 @@ private async Task CreateEnvironment(string application, string environm } } - private void AddAdditionalOptions(IList settings, bool createEnvironmentMode, bool isWindowsEnvironment) + private List AddAdditionalOptions(List settings, bool createEnvironmentMode, bool isWindowsEnvironment) { + if (settings == null) + settings = new List(); + var additionalOptions = this.GetKeyValuePairOrDefault(this.DeployEnvironmentOptions.AdditionalOptions, EBDefinedCommandOptions.ARGUMENT_EB_ADDITIONAL_OPTIONS, false); if (additionalOptions != null && additionalOptions.Count > 0) { @@ -450,6 +455,7 @@ private void AddAdditionalOptions(IList settings, bo throw new ToolsException("Additional option \"" + kvp.Key + "=" + kvp.Value + "\" in incorrect format. Format should be ,=.", ToolsException.CommonErrorCode.DefaultsParseFail); } + settings.Add(new ConfigurationOptionSetting { Namespace = tokens[0], @@ -582,6 +588,8 @@ private void AddAdditionalOptions(IList settings, bo Value = enableStickySessions.Value.ToString(CultureInfo.InvariantCulture).ToLowerInvariant() }); } + + return settings; } private async Task UpdateEnvironment(EnvironmentDescription environmentDescription, string versionLabel) @@ -594,7 +602,7 @@ private async Task UpdateEnvironment(EnvironmentDescription environmentD VersionLabel = versionLabel }; - AddAdditionalOptions(updateRequest.OptionSettings, false, EBUtilities.IsSolutionStackWindows(environmentDescription.SolutionStackName)); + updateRequest.OptionSettings = AddAdditionalOptions(updateRequest.OptionSettings, false, EBUtilities.IsSolutionStackWindows(environmentDescription.SolutionStackName)); try { @@ -626,17 +634,21 @@ private void DetermineEnvironment(out string environmentType, out string loadBal private async Task DoesApplicationExist(string applicationName) { var request = new DescribeApplicationsRequest(); + if (request.ApplicationNames == null) + request.ApplicationNames = new List(); request.ApplicationNames.Add(applicationName); var response = await this.EBClient.DescribeApplicationsAsync(request); - return response.Applications.Count == 1; + return response.Applications != null && response.Applications.Count == 1; } private async Task GetEnvironmentDescription(string applicationName, string environmentName) { var request = new DescribeEnvironmentsRequest { ApplicationName = applicationName }; + if (request.EnvironmentNames == null) + request.EnvironmentNames = new List(); request.EnvironmentNames.Add(environmentName); var response = await this.EBClient.DescribeEnvironmentsAsync(request); - if (response.Environments.Where(x => x.Status != EnvironmentStatus.Terminated && x.Status != EnvironmentStatus.Terminating).Count() != 1) + if (response.Environments == null || response.Environments.Where(x => x.Status != EnvironmentStatus.Terminated && x.Status != EnvironmentStatus.Terminating).Count() != 1) return null; var environment = response.Environments[0]; @@ -659,7 +671,7 @@ private async Task WaitForDeploymentCompletionAsync(string applicationName { ApplicationName = applicationName, EnvironmentName = environmentName, - StartTimeUtc = startingEventDate + StartTime = startingEventDate }; var success = true; @@ -670,14 +682,14 @@ private async Task WaitForDeploymentCompletionAsync(string applicationName Thread.Sleep(5000); var responseEnvironments = await this.EBClient.DescribeEnvironmentsAsync(requestEnvironment); - if (responseEnvironments.Environments.Count == 0) + if (responseEnvironments.Environments == null || responseEnvironments.Environments.Count == 0) throw new ElasticBeanstalkExceptions("Failed to find environment when waiting for deployment completion", ElasticBeanstalkExceptions.EBCode.FailedToFindEnvironment ); environment = responseEnvironments.Environments[0]; - requestEvents.StartTimeUtc = lastPrintedEventDate; + requestEvents.StartTime = lastPrintedEventDate; var responseEvents = await this.EBClient.DescribeEventsAsync(requestEvents); - if(responseEvents.Events.Count > 0) + if(responseEvents.Events != null && responseEvents.Events.Count > 0) { for(int i = responseEvents.Events.Count - 1; i >= 0; i--) { @@ -685,7 +697,7 @@ private async Task WaitForDeploymentCompletionAsync(string applicationName if (evnt.EventDate <= lastPrintedEventDate) continue; - this.Logger?.WriteLine(evnt.EventDate.ToLocalTime() + " " + evnt.Severity + " " + evnt.Message); + this.Logger?.WriteLine(evnt.EventDate.Value.ToLocalTime() + " " + evnt.Severity + " " + evnt.Message); if(evnt.Message.StartsWith("Failed to deploy application", StringComparison.OrdinalIgnoreCase) || evnt.Message.StartsWith("Failed to launch environment", StringComparison.OrdinalIgnoreCase) || evnt.Message.StartsWith("Error occurred during build: Command hooks failed", StringComparison.OrdinalIgnoreCase)) @@ -694,7 +706,7 @@ private async Task WaitForDeploymentCompletionAsync(string applicationName } } - lastPrintedEventDate = responseEvents.Events[0].EventDate; + lastPrintedEventDate = responseEvents.Events[0].EventDate.Value; } } while (environment.Status == EnvironmentStatus.Launching || environment.Status == EnvironmentStatus.Updating); @@ -716,10 +728,10 @@ private async Task GetLatestEventDateAsync(string application, string }; var response = await this.EBClient.DescribeEventsAsync(request); - if (response.Events.Count == 0) + if (response.Events == null || response.Events.Count == 0) return DateTime.Now; - return response.Events[0].EventDate; + return response.Events[0].EventDate.Value; } private async Task UploadDeploymentPackageAsync(string application, string versionLabel, string deploymentPackage) @@ -749,7 +761,7 @@ private async Task UploadDeploymentPackageAsync(string application, } - protected override void SaveConfigFile(JsonData data) + protected override void SaveConfigFile(Dictionary data) { this.DeployEnvironmentOptions.PersistSettings(this, data); } diff --git a/src/Amazon.ElasticBeanstalk.Tools/Commands/EBBaseCommand.cs b/src/Amazon.ElasticBeanstalk.Tools/Commands/EBBaseCommand.cs index dc8b2411..246acd11 100644 --- a/src/Amazon.ElasticBeanstalk.Tools/Commands/EBBaseCommand.cs +++ b/src/Amazon.ElasticBeanstalk.Tools/Commands/EBBaseCommand.cs @@ -76,6 +76,8 @@ public async Task> FindSolutionStacksAsync() var solutionStacks = new List(); var allSolutionStacks = (await this.EBClient.ListAvailableSolutionStacksAsync()).SolutionStacks; + if (allSolutionStacks == null) + return new List(); foreach (var stack in allSolutionStacks.OrderByDescending(x => x)) { if (EBUtilities.IsSolutionStackWindows(stack) || EBUtilities.IsSolutionStackLinuxNETCore(stack)) diff --git a/src/Amazon.ElasticBeanstalk.Tools/Commands/ListEnvironmentsCommand.cs b/src/Amazon.ElasticBeanstalk.Tools/Commands/ListEnvironmentsCommand.cs index 0ebf6466..232b2a04 100644 --- a/src/Amazon.ElasticBeanstalk.Tools/Commands/ListEnvironmentsCommand.cs +++ b/src/Amazon.ElasticBeanstalk.Tools/Commands/ListEnvironmentsCommand.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using ThirdParty.Json.LitJson; namespace Amazon.ElasticBeanstalk.Tools.Commands { @@ -45,12 +44,15 @@ protected override async Task PerformActionAsync() NextToken = response.NextToken }); - foreach(var environment in response.Environments) + if (response.Environments != null) { - if (environment.Status == EnvironmentStatus.Terminated) - continue; + foreach(var environment in response.Environments) + { + if (environment.Status == EnvironmentStatus.Terminated) + continue; - this.Logger?.WriteLine((environment.EnvironmentName + " (" + environment.Status + "/" + environment.Health + ")").PadRight(45) + " http://" + (environment.CNAME ?? environment.EndpointURL) + "/"); + this.Logger?.WriteLine((environment.EnvironmentName + " (" + environment.Status + "/" + environment.Health + ")").PadRight(45) + " http://" + (environment.CNAME ?? environment.EndpointURL) + "/"); + } } } while (!string.IsNullOrEmpty(response.NextToken)); @@ -63,7 +65,7 @@ protected override async Task PerformActionAsync() return true; } - protected override void SaveConfigFile(JsonData data) + protected override void SaveConfigFile(Dictionary data) { } diff --git a/src/Amazon.ElasticBeanstalk.Tools/Commands/PackageCommand.cs b/src/Amazon.ElasticBeanstalk.Tools/Commands/PackageCommand.cs index 917d6ccc..ef50076c 100644 --- a/src/Amazon.ElasticBeanstalk.Tools/Commands/PackageCommand.cs +++ b/src/Amazon.ElasticBeanstalk.Tools/Commands/PackageCommand.cs @@ -5,7 +5,6 @@ using Amazon.Common.DotNetCli.Tools; using Amazon.Common.DotNetCli.Tools.Options; -using ThirdParty.Json.LitJson; namespace Amazon.ElasticBeanstalk.Tools.Commands { @@ -104,7 +103,7 @@ protected override Task PerformActionAsync() return Task.FromResult(true); } - protected override void SaveConfigFile(JsonData data) + protected override void SaveConfigFile(Dictionary data) { this.DeployEnvironmentOptions.PersistSettings(this, data); data.SetIfNotNull(EBDefinedCommandOptions.ARGUMENT_OUTPUT_PACKAGE.ConfigFileKey, this.GetStringValueOrDefault(this.OutputPackageFileName, EBDefinedCommandOptions.ARGUMENT_OUTPUT_PACKAGE, false)); diff --git a/src/Amazon.ElasticBeanstalk.Tools/EBUtilities.cs b/src/Amazon.ElasticBeanstalk.Tools/EBUtilities.cs index be0e79b6..9183de32 100644 --- a/src/Amazon.ElasticBeanstalk.Tools/EBUtilities.cs +++ b/src/Amazon.ElasticBeanstalk.Tools/EBUtilities.cs @@ -4,11 +4,10 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Text.Json; using Amazon.Common.DotNetCli.Tools; -using Amazon.Common.DotNetCli.Tools.Options; using Amazon.ElasticBeanstalk.Model; using Amazon.ElasticBeanstalk.Tools.Commands; -using ThirdParty.Json.LitJson; namespace Amazon.ElasticBeanstalk.Tools { @@ -25,48 +24,52 @@ public static void SetupAWSDeploymentManifest(IToolLogger logger, EBBaseCommand { logger?.WriteLine("Updating existing deployment manifest"); - Func getOrCreateNode = (name, node) => + var data = new Dictionary(); + string existingJson = File.ReadAllText(pathToManifest); + using (JsonDocument doc = JsonDocument.Parse(existingJson)) { - JsonData child = node[name] as JsonData; - if (child == null) + foreach (JsonProperty prop in doc.RootElement.EnumerateObject()) { - child = new JsonData(); - node[name] = child; + data[prop.Name] = prop.Value.GetJsonValue(); } - return child; - }; + } - JsonData root = JsonMapper.ToObject(File.ReadAllText(pathToManifest)); - if (root["manifestVersion"] == null || !root["manifestVersion"].IsInt) + if (!data.ContainsKey("manifestVersion") || !(data["manifestVersion"] is int)) { - root["manifestVersion"] = 1; + data["manifestVersion"] = 1; } - JsonData deploymentNode = getOrCreateNode("deployments", root); + if (!data.ContainsKey("deployments")) + data["deployments"] = new Dictionary(); + var deployments = (Dictionary)data["deployments"]; - JsonData aspNetCoreWebNode = getOrCreateNode("aspNetCoreWeb", deploymentNode); + if (!deployments.ContainsKey("aspNetCoreWeb")) + deployments["aspNetCoreWeb"] = new List(); + var aspNetCoreWeb = (List)deployments["aspNetCoreWeb"]; - JsonData appNode; - if (aspNetCoreWebNode.GetJsonType() == JsonType.None || aspNetCoreWebNode.Count == 0) + Dictionary appNode; + if (aspNetCoreWeb.Count == 0) { - appNode = new JsonData(); - aspNetCoreWebNode.Add(appNode); + appNode = new Dictionary(); + aspNetCoreWeb.Add(appNode); } else - appNode = aspNetCoreWebNode[0]; - + appNode = (Dictionary)aspNetCoreWeb[0]; - if (appNode["name"] == null || !appNode["name"].IsString || string.IsNullOrEmpty((string)appNode["name"])) + if (!appNode.ContainsKey("name") || !(appNode["name"] is string name) || string.IsNullOrEmpty(name)) { appNode["name"] = "app"; } - JsonData parametersNode = getOrCreateNode("parameters", appNode); - parametersNode["appBundle"] = "."; - parametersNode["iisPath"] = iisAppPath; - parametersNode["iisWebSite"] = iisWebSite; + if (!appNode.ContainsKey("parameters")) + appNode["parameters"] = new Dictionary(); + var parameters = (Dictionary)appNode["parameters"]; + parameters["appBundle"] = "."; + parameters["iisPath"] = iisAppPath; + parameters["iisWebSite"] = iisWebSite; - manifest = root.ToJson(); + var jsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; + manifest = JsonSerializer.Serialize(data, jsonSerializerOptions); } else { @@ -155,20 +158,25 @@ public static bool IsSelfContainedPublish(string runtimeConfigFile) runtimeConfigFile = File.ReadAllText(runtimeConfigFile); } + try + { + using (JsonDocument doc = JsonDocument.Parse(runtimeConfigFile)) + { + if (!doc.RootElement.TryGetProperty("runtimeOptions", out JsonElement runtimeOptions)) + return false; + + if (!runtimeOptions.TryGetProperty("includedFrameworks", out JsonElement includedFrameworks)) + return false; - JsonData root = JsonMapper.ToObject(runtimeConfigFile); - var runtimeOptions = root["runtimeOptions"] as JsonData; - if(runtimeOptions == null) + return includedFrameworks.ValueKind == JsonValueKind.Array && includedFrameworks.GetArrayLength() > 0; + } + } + catch { return false; } - - var includedFrameworks = runtimeOptions["includedFrameworks"] as JsonData; - - return includedFrameworks != null && includedFrameworks.Count > 0; } - public static string FindExistingValue(this List settings, string ns, string name) { var setting = settings?.FirstOrDefault(x => string.Equals(x.Namespace, ns, StringComparison.InvariantCulture) && string.Equals(x.OptionName, name, StringComparison.InvariantCulture)); diff --git a/src/Amazon.ElasticBeanstalk.Tools/ElasticBeanstalkToolsDefaults.cs b/src/Amazon.ElasticBeanstalk.Tools/ElasticBeanstalkToolsDefaults.cs index c29bef18..31001237 100644 --- a/src/Amazon.ElasticBeanstalk.Tools/ElasticBeanstalkToolsDefaults.cs +++ b/src/Amazon.ElasticBeanstalk.Tools/ElasticBeanstalkToolsDefaults.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; using System.Text; - -using ThirdParty.Json.LitJson; +using System.Text.Json; using Amazon.Common.DotNetCli.Tools; namespace Amazon.ElasticBeanstalk.Tools @@ -17,11 +16,11 @@ public ElasticBeanstalkToolsDefaults() } public ElasticBeanstalkToolsDefaults(string sourceFile) - : this(new JsonData(), sourceFile) + : this(new JsonElement(), sourceFile) { } - public ElasticBeanstalkToolsDefaults(JsonData data, string sourceFile) + public ElasticBeanstalkToolsDefaults(JsonElement data, string sourceFile) : base(data, sourceFile) { } diff --git a/src/Amazon.Lambda.Tools/Amazon.Lambda.Tools.csproj b/src/Amazon.Lambda.Tools/Amazon.Lambda.Tools.csproj index 5bc45741..eb3da792 100644 --- a/src/Amazon.Lambda.Tools/Amazon.Lambda.Tools.csproj +++ b/src/Amazon.Lambda.Tools/Amazon.Lambda.Tools.csproj @@ -1,9 +1,9 @@ - + Amazon.Lambda.Tools adds commands to the dotnet cli to deploy AWS Lambda functions. Amazon.Lambda.Tools - netcoreapp3.1;net6.0;net8.0 + net6.0;net8.0 dotnet-lambda Exe Amazon.Lambda.Tools @@ -16,6 +16,11 @@ AWS Lambda Tools for .NET CLI false 5.13.0 + + 7.3 @@ -36,14 +41,15 @@ - - - - + + + + - 1701;1702;1705;1591 + true + 1701;1702;1705;1591;NETSDK1138