Skip to content

Commit

Permalink
feat: Add type hints on child settings
Browse files Browse the repository at this point in the history
  • Loading branch information
philasmar committed Dec 9, 2021
1 parent 1a1fd92 commit a35ad22
Show file tree
Hide file tree
Showing 20 changed files with 261 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ public async Task<object> Execute(Recommendation recommendation, OptionSettingIt
{
const string NO_VALUE = "*** Do not select table ***";
var currentValue = recommendation.GetOptionSettingValue(optionSetting);
var typeHintData = optionSetting.GetTypeHintData<DynamoDBTableTypeHintData>();
var tables = await GetData();

tables.Add(NO_VALUE);
if (typeHintData?.AllowNoValue ?? false)
tables.Add(NO_VALUE);

var userResponse = _consoleUtilities.AskUserToChoose(
values: tables,
title: "Select a DynamoDB table:",
Expand Down
93 changes: 93 additions & 0 deletions src/AWS.Deploy.CLI/Commands/TypeHints/InstanceTypeCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Amazon.EC2.Model;
using AWS.Deploy.Common;
using AWS.Deploy.Common.Recipes;
using AWS.Deploy.Common.TypeHintData;
using AWS.Deploy.Orchestration.Data;

namespace AWS.Deploy.CLI.Commands.TypeHints
{
public class InstanceTypeCommand : ITypeHintCommand
{
private readonly IAWSResourceQueryer _awsResourceQueryer;
private readonly IConsoleUtilities _consoleUtilities;

public InstanceTypeCommand(IAWSResourceQueryer awsResourceQueryer, IConsoleUtilities consoleUtilities)
{
_awsResourceQueryer = awsResourceQueryer;
_consoleUtilities = consoleUtilities;
}

private async Task<List<InstanceTypeInfo>?> GetData(Recommendation recommendation, OptionSettingItem optionSetting)
{
return await _awsResourceQueryer.ListOfAvailableInstanceTypes();
}

public async Task<List<TypeHintResource>?> GetResources(Recommendation recommendation, OptionSettingItem optionSetting)
{
var instanceType = await GetData(recommendation, optionSetting);
return instanceType?
.Select(x => new TypeHintResource(x.InstanceType.Value, x.InstanceType.Value))
.Distinct()
.OrderBy(x => x)
.ToList();
}

public async Task<object> Execute(Recommendation recommendation, OptionSettingItem optionSetting)
{
var instanceTypes = await GetData(recommendation, optionSetting);
var instanceTypeDefaultValue = recommendation.GetOptionSettingDefaultValue<string>(optionSetting);
if (instanceTypes == null)
{
return _consoleUtilities.AskUserForValue("Select EC2 Instance Type:", instanceTypeDefaultValue ?? string.Empty, true);
}

var freeTierEligibleAnswer = _consoleUtilities.AskYesNoQuestion("Do you want the EC2 instance to be free tier eligible?", "true");
var freeTierEligible = freeTierEligibleAnswer == YesNo.Yes;

var architectureAllowedValues = new List<string> { "x86_64", "arm64"};

var architecture = _consoleUtilities.AskUserToChoose(architectureAllowedValues, "The architecture of the EC2 instances created for the environment.", "x86_64");

var cpuCores = instanceTypes
.Where(x => x.FreeTierEligible.Equals(freeTierEligible))
.Where(x => x.ProcessorInfo.SupportedArchitectures.Contains(architecture))
.Select(x => x.VCpuInfo.DefaultCores).Distinct().OrderBy(x => x).ToList();

if (cpuCores.Count == 0)
return _consoleUtilities.AskUserForValue("Select EC2 Instance Type:", instanceTypeDefaultValue ?? string.Empty, true);

var cpuCoreCount = int.Parse(_consoleUtilities.AskUserToChoose(cpuCores.Select(x => x.ToString()).ToList(), "Select EC2 Instance CPU Cores:", "1"));

var memory = instanceTypes
.Where(x => x.FreeTierEligible.Equals(freeTierEligible))
.Where(x => x.ProcessorInfo.SupportedArchitectures.Contains(architecture))
.Where(x => x.VCpuInfo.DefaultCores.Equals(cpuCoreCount))
.Select(x => x.MemoryInfo.SizeInMiB).Distinct().OrderBy(x => x).ToList();

if (memory.Count == 0)
return _consoleUtilities.AskUserForValue("Select EC2 Instance Type:", instanceTypeDefaultValue ?? string.Empty, true);

var memoryCount = _consoleUtilities.AskUserToChoose(memory.Select(x => x.ToString()).ToList(), "Select EC2 Instance Memory:", "1024");

var availableInstanceTypes = instanceTypes
.Where(x => x.FreeTierEligible.Equals(freeTierEligible))
.Where(x => x.ProcessorInfo.SupportedArchitectures.Contains(architecture))
.Where(x => x.VCpuInfo.DefaultCores.Equals(cpuCoreCount))
.Where(x => x.MemoryInfo.SizeInMiB.Equals(long.Parse(memoryCount)))
.Select(x => x.InstanceType.Value).Distinct().OrderBy(x => x).ToList();

if (availableInstanceTypes.Count == 0)
return _consoleUtilities.AskUserForValue("Select EC2 Instance Type:", instanceTypeDefaultValue ?? string.Empty, true);

var userResponse = _consoleUtilities.AskUserToChoose(availableInstanceTypes, "Select EC2 Instance Type:", availableInstanceTypes.First());

return userResponse;
}
}
}
5 changes: 4 additions & 1 deletion src/AWS.Deploy.CLI/Commands/TypeHints/S3BucketNameCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ public async Task<object> Execute(Recommendation recommendation, OptionSettingIt
{
const string NO_VALUE = "*** Do not select bucket ***";
var currentValue = recommendation.GetOptionSettingValue(optionSetting);
var typeHintData = optionSetting.GetTypeHintData<S3BucketNameTypeHintData>();
var buckets = (await GetData()).Select(bucket => bucket.BucketName).ToList();

buckets.Add(NO_VALUE);
if (typeHintData?.AllowNoValue ?? false)
buckets.Add(NO_VALUE);

var userResponse = _consoleUtilities.AskUserToChoose(
values: buckets,
title: "Select a S3 bucket:",
Expand Down
4 changes: 3 additions & 1 deletion src/AWS.Deploy.CLI/Commands/TypeHints/SNSTopicArnsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public async Task<object> Execute(Recommendation recommendation, OptionSettingIt
{
const string NO_VALUE = "*** Do not select topic ***";
var currentValue = recommendation.GetOptionSettingValue(optionSetting);
var typeHintData = optionSetting.GetTypeHintData<SNSTopicArnsTypeHintData>();
var currentValueStr = currentValue.ToString() ?? string.Empty;
var topicArns = await GetResources(recommendation, optionSetting);

Expand All @@ -43,7 +44,8 @@ public async Task<object> Execute(Recommendation recommendation, OptionSettingIt
currentName = currentValueStr.Substring(currentValueStr.LastIndexOf(':') + 1);
}

topicNames.Add(NO_VALUE);
if (typeHintData?.AllowNoValue ?? false)
topicNames.Add(NO_VALUE);
var userResponse = _consoleUtilities.AskUserToChoose(
values: topicNames,
title: "Select a SNS topic:",
Expand Down
4 changes: 3 additions & 1 deletion src/AWS.Deploy.CLI/Commands/TypeHints/SQSQueueUrlCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public async Task<object> Execute(Recommendation recommendation, OptionSettingIt
{
const string NO_VALUE = "*** Do not select queue ***";
var currentValue = recommendation.GetOptionSettingValue(optionSetting);
var typeHintData = optionSetting.GetTypeHintData<SQSQueueUrlTypeHintData>();
var currentValueStr = currentValue.ToString() ?? string.Empty;
var queueUrls = await GetResources(recommendation, optionSetting);

Expand All @@ -43,7 +44,8 @@ public async Task<object> Execute(Recommendation recommendation, OptionSettingIt
currentName = currentValueStr.Substring(currentValueStr.LastIndexOf('/') + 1);
}

queueNames.Add(NO_VALUE);
if (typeHintData?.AllowNoValue ?? false)
queueNames.Add(NO_VALUE);
var userResponse = _consoleUtilities.AskUserToChoose(
values: queueNames,
title: "Select a SQS queue:",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,27 @@ public TypeHintCommandFactory(IToolInteractiveService toolInteractiveService, IA
_commands = new Dictionary<OptionSettingTypeHint, ITypeHintCommand>
{
{ OptionSettingTypeHint.BeanstalkApplication, new BeanstalkApplicationCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.ExistingBeanstalkApplication, new BeanstalkApplicationCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.BeanstalkEnvironment, new BeanstalkEnvironmentCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.DotnetBeanstalkPlatformArn, new DotnetBeanstalkPlatformArnCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.EC2KeyPair, new EC2KeyPairCommand(toolInteractiveService, awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.IAMRole, new IAMRoleCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.ExistingIAMRole, new IAMRoleCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.Vpc, new VpcCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.ExistingVpc, new VpcCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.DotnetPublishAdditionalBuildArguments, new DotnetPublishArgsCommand(consoleUtilities) },
{ OptionSettingTypeHint.DotnetPublishSelfContainedBuild, new DotnetPublishSelfContainedBuildCommand(consoleUtilities) },
{ OptionSettingTypeHint.DotnetPublishBuildConfiguration, new DotnetPublishBuildConfigurationCommand(consoleUtilities) },
{ OptionSettingTypeHint.DockerExecutionDirectory, new DockerExecutionDirectoryCommand(consoleUtilities, directoryManager) },
{ OptionSettingTypeHint.DockerBuildArgs, new DockerBuildArgsCommand(consoleUtilities) },
{ OptionSettingTypeHint.ECSCluster, new ECSClusterCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.ExistingECSCluster, new ECSClusterCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.ExistingApplicationLoadBalancer, new ExistingApplicationLoadBalancerCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.DynamoDBTableName, new DynamoDBTableCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.SQSQueueUrl, new SQSQueueUrlCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.SNSTopicArn, new SNSTopicArnsCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.S3BucketName, new S3BucketNameCommand(awsResourceQueryer, consoleUtilities) },
{ OptionSettingTypeHint.InstanceType, new InstanceTypeCommand(awsResourceQueryer, consoleUtilities) },
};
}

Expand Down
6 changes: 5 additions & 1 deletion src/AWS.Deploy.Common/Recipes/OptionSettingTypeHint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public enum OptionSettingTypeHint
SQSQueueUrl,
SNSTopicArn,
S3BucketName,
BeanstalkRollingUpdates
BeanstalkRollingUpdates,
ExistingIAMRole,
ExistingECSCluster,
ExistingVpc,
ExistingBeanstalkApplication
};
}
23 changes: 23 additions & 0 deletions src/AWS.Deploy.Common/TypeHintData/DynamoDBTableTypeHintData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
// SPDX-License-Identifier: Apache-2.0

using AWS.Deploy.Common.Recipes;

namespace AWS.Deploy.Common.TypeHintData
{
/// <summary>
/// Holds additional data for <see cref="OptionSettingTypeHint.DynamoDBTableName"/> processing.
/// </summary>
public class DynamoDBTableTypeHintData
{
/// <summary>
/// Determines whether to allow no value or not.
/// </summary>
public bool AllowNoValue { get; set; }

public DynamoDBTableTypeHintData(bool allowNoValue)
{
AllowNoValue = allowNoValue;
}
}
}
23 changes: 23 additions & 0 deletions src/AWS.Deploy.Common/TypeHintData/S3BucketNameTypeHintData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
// SPDX-License-Identifier: Apache-2.0

using AWS.Deploy.Common.Recipes;

namespace AWS.Deploy.Common.TypeHintData
{
/// <summary>
/// Holds additional data for <see cref="OptionSettingTypeHint.S3BucketName"/> processing.
/// </summary>
public class S3BucketNameTypeHintData
{
/// <summary>
/// Determines whether to allow no value or not.
/// </summary>
public bool AllowNoValue { get; set; }

public S3BucketNameTypeHintData(bool allowNoValue)
{
AllowNoValue = allowNoValue;
}
}
}
23 changes: 23 additions & 0 deletions src/AWS.Deploy.Common/TypeHintData/SNSTopicArnsTypeHintData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
// SPDX-License-Identifier: Apache-2.0

using AWS.Deploy.Common.Recipes;

namespace AWS.Deploy.Common.TypeHintData
{
/// <summary>
/// Holds additional data for <see cref="OptionSettingTypeHint.SNSTopicArn"/> processing.
/// </summary>
public class SNSTopicArnsTypeHintData
{
/// <summary>
/// Determines whether to allow no value or not.
/// </summary>
public bool AllowNoValue { get; set; }

public SNSTopicArnsTypeHintData(bool allowNoValue)
{
AllowNoValue = allowNoValue;
}
}
}
23 changes: 23 additions & 0 deletions src/AWS.Deploy.Common/TypeHintData/SQSQueueUrlTypeHintData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
// SPDX-License-Identifier: Apache-2.0

using AWS.Deploy.Common.Recipes;

namespace AWS.Deploy.Common.TypeHintData
{
/// <summary>
/// Holds additional data for <see cref="OptionSettingTypeHint.SQSQueueUrl"/> processing.
/// </summary>
public class SQSQueueUrlTypeHintData
{
/// <summary>
/// Determines whether to allow no value or not.
/// </summary>
public bool AllowNoValue { get; set; }

public SQSQueueUrlTypeHintData(bool allowNoValue)
{
AllowNoValue = allowNoValue;
}
}
}
15 changes: 15 additions & 0 deletions src/AWS.Deploy.Orchestration/Data/AWSResourceQueryer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ namespace AWS.Deploy.Orchestration.Data
{
public interface IAWSResourceQueryer
{
Task<List<InstanceTypeInfo>> ListOfAvailableInstanceTypes();
Task<Amazon.AppRunner.Model.Service> DescribeAppRunnerService(string serviceArn);
Task<List<StackResource>> DescribeCloudFormationResources(string stackName);
Task<EnvironmentDescription> DescribeElasticBeanstalkEnvironment(string environmentId);
Expand Down Expand Up @@ -78,6 +79,20 @@ public AWSResourceQueryer(IAWSClientFactory awsClientFactory)
_awsClientFactory = awsClientFactory;
}

public async Task<List<InstanceTypeInfo>> ListOfAvailableInstanceTypes()
{
var ec2Client = _awsClientFactory.GetAWSClient<IAmazonEC2>();
var instanceTypes = new List<InstanceTypeInfo>();
var listInstanceTypesPaginator = ec2Client.Paginators.DescribeInstanceTypes(new DescribeInstanceTypesRequest());

await foreach (var response in listInstanceTypesPaginator.Responses)
{
instanceTypes.AddRange(response.InstanceTypes);
}

return instanceTypes;
}

public async Task<Amazon.AppRunner.Model.Service> DescribeAppRunnerService(string serviceArn)
{
var appRunnerClient = _awsClientFactory.GetAWSClient<Amazon.AppRunner.IAmazonAppRunner>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@
"Name": "Existing Role ARN",
"Description": "The ARN of the existing role to use.",
"Type": "String",
"TypeHint": "ExistingIAMRole",
"TypeHintData": {
"ServicePrincipal": "tasks.apprunner.amazonaws.com"
},
"AdvancedSetting": false,
"Updatable": true,
"DependsOn": [
Expand Down Expand Up @@ -145,6 +149,10 @@
"Name": "Existing Role ARN",
"Description": "The ARN of the existing role to use.",
"Type": "String",
"TypeHint": "ExistingIAMRole",
"TypeHintData": {
"ServicePrincipal": "build.apprunner.amazonaws.com"
},
"AdvancedSetting": false,
"Updatable": true,
"DependsOn": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
"Name": "Existing Cluster ARN",
"Description": "The ARN of the existing cluster to use.",
"Type": "String",
"TypeHint": "ExistingECSCluster",
"AdvancedSetting": false,
"Updatable": false,
"Validators": [
Expand Down Expand Up @@ -202,6 +203,10 @@
"Name": "Existing Role ARN",
"Description": "The ARN of the existing role to use.",
"Type": "String",
"TypeHint": "ExistingIAMRole",
"TypeHintData": {
"ServicePrincipal": "ecs-tasks.amazonaws.com"
},
"AdvancedSetting": false,
"Updatable": true,
"Validators": [
Expand Down Expand Up @@ -261,6 +266,7 @@
"Name": "Existing VPC ID",
"Description": "The ID of the existing VPC to use.",
"Type": "String",
"TypeHint": "ExistingVpc",
"DefaultValue": null,
"AdvancedSetting": false,
"Updatable": false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"Name": "Application Name",
"Description": "The Elastic Beanstalk application name.",
"Type": "String",
"TypeHint": "ExistingBeanstalkApplication",
"DefaultValue": "{StackName}",
"AdvancedSetting": false,
"Updatable": false,
Expand Down Expand Up @@ -139,7 +140,6 @@
"Description": "The EC2 instance type of the EC2 instances created for the environment.",
"Type": "String",
"TypeHint": "InstanceType",
"DefaultValue": "",
"AdvancedSetting": true,
"Updatable": true
},
Expand Down Expand Up @@ -211,6 +211,10 @@
"Name": "Existing Role ARN",
"Description": "The ARN of the existing role to use.",
"Type": "String",
"TypeHint": "ExistingIAMRole",
"TypeHintData": {
"ServicePrincipal": "elasticbeanstalk.amazonaws.com"
},
"AdvancedSetting": false,
"Updatable": false,
"DependsOn": [
Expand Down
Loading

0 comments on commit a35ad22

Please sign in to comment.