// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\r // SPDX-License-Identifier: Apache-2.0 using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace AWS.Deploy.Common.Recipes.Validation { /// /// Enforces Fargate cpu and memory conditional requirements: /// CPU value Memory value (MiB) /// 256 (.25 vCPU) 512 (0.5 GB), 1024 (1 GB), 2048 (2 GB) /// 512 (.5 vCPU) 1024 (1 GB), 2048 (2 GB), 3072 (3 GB), 4096 (4 GB) /// 1024 (1 vCPU) 2048 (2 GB), 3072 (3 GB), 4096 (4 GB), 5120 (5 GB), 6144 (6 GB), 7168 (7 GB), 8192 (8 GB) /// 2048 (2 vCPU) Between 4096 (4 GB) and 16384 (16 GB) in increments of 1024 (1 GB) /// 4096 (4 vCPU) Between 8192 (8 GB) and 30720 (30 GB) in increments of 1024 (1 GB) /// /// See https://docs.aws.amazon.com/AmazonECS/latest/userguide/task_definition_parameters.html#task_size /// for more details. /// public class FargateTaskCpuMemorySizeValidator : IRecipeValidator { private readonly IOptionSettingHandler _optionSettingHandler; public FargateTaskCpuMemorySizeValidator(IOptionSettingHandler optionSettingHandler) { _optionSettingHandler = optionSettingHandler; } private readonly Dictionary _cpuMemoryMap = new() { { "256", new[] { "512", "1024", "2048" } }, { "512", new[] { "1024", "2048", "3072", "4096" } }, { "1024", new[] { "2048", "3072", "4096", "5120", "6144", "7168", "8192" } }, { "2048", BuildMemoryArray(4096, 16384).ToArray() }, { "4096", BuildMemoryArray(8192, 30720).ToArray()} }; private static IEnumerable BuildMemoryArray(int start, int end, int increment = 1024) { while (start <= end) { yield return start.ToString(); start += increment; } } public string CpuOptionSettingsId { get; set; } = "TaskCpu"; public string MemoryOptionSettingsId { get; set; } = "TaskMemory"; /// /// Supports replacement tokens {{cpu}}, {{memory}}, and {{memoryList}} /// public string ValidationFailedMessage { get; set; } = "Cpu value {{cpu}} is not compatible with memory value {{memory}}. Allowed values are {{memoryList}}"; public string? InvalidCpuValueValidationFailedMessage { get; set; } /// public Task Validate(Recommendation recommendation, IDeployToolValidationContext deployValidationContext) { string? cpu; string? memory; try { cpu = _optionSettingHandler.GetOptionSettingValue(recommendation, _optionSettingHandler.GetOptionSetting(recommendation, CpuOptionSettingsId)); memory = _optionSettingHandler.GetOptionSettingValue(recommendation, _optionSettingHandler.GetOptionSetting(recommendation, MemoryOptionSettingsId)); } catch (OptionSettingItemDoesNotExistException) { return Task.FromResult(ValidationResult.Failed("Could not find a valid value for Task CPU or Task Memory " + "as part of of the ECS Fargate deployment configuration. Please provide a valid value and try again.")); } if (cpu == null) return ValidationResult.FailedAsync("Task CPU is null."); if (memory == null) return ValidationResult.FailedAsync("Task Memory is null."); if (!_cpuMemoryMap.ContainsKey(cpu)) { // this could happen, but shouldn't. // either there is mismatch between _cpuMemoryMap and the AllowedValues // or the UX flow calling in here doesn't enforce AllowedValues. var message = InvalidCpuValueValidationFailedMessage?.Replace("{{cpu}}", cpu); return Task.FromResult(ValidationResult.Failed(message?? "Cpu validation failed")); } var validMemoryValues = _cpuMemoryMap[cpu]; if (validMemoryValues.Contains(memory)) { return Task.FromResult(ValidationResult.Valid()); } var failed = ValidationFailedMessage .Replace("{{cpu}}", cpu) .Replace("{{memory}}", memory) .Replace("{{memoryList}}", string.Join(", ", validMemoryValues)); return Task.FromResult(ValidationResult.Failed(failed)); } } }