public async Task ClearOptionSettingValue_String() { var interactiveServices = new TestToolInteractiveServiceImpl(new List <string> { "<reset>" }); var consoleUtilities = new ConsoleUtilities(interactiveServices); var projectPath = SystemIOUtilities.ResolvePath("WebAppNoDockerFile"); var engine = new RecommendationEngine(new[] { RecipeLocator.FindRecipeDefinitionsPath() }, new Orchestrator.OrchestratorSession()); var recommendations = await engine.ComputeRecommendations(projectPath, new Dictionary <string, string>()); var fargateRecommendation = recommendations.First(r => r.Recipe.Id == Constants.ASPNET_CORE_ASPNET_CORE_FARGATE_RECIPE_ID); var ecsServiceNameOptionSetting = fargateRecommendation.Recipe.OptionSettings.First(optionSetting => optionSetting.Id.Equals("ECSServiceName")); var originalDefaultValue = fargateRecommendation.GetOptionSettingDefaultValue <string>(ecsServiceNameOptionSetting); ecsServiceNameOptionSetting.SetValueOverride("TestService"); Assert.Equal("TestService", fargateRecommendation.GetOptionSettingValue <string>(ecsServiceNameOptionSetting)); ecsServiceNameOptionSetting.SetValueOverride(consoleUtilities.AskUserForValue("Title", "TestService", true, originalDefaultValue)); Assert.Equal(originalDefaultValue, fargateRecommendation.GetOptionSettingValue <string>(ecsServiceNameOptionSetting)); }
public void ShouldIncludeTests(RuleEffect effect, bool testPass, bool expectedResult) { var engine = new RecommendationEngine(new[] { RecipeLocator.FindRecipeDefinitionsPath() }, new Orchestrator.OrchestratorSession()); Assert.Equal(expectedResult, engine.ShouldInclude(effect, testPass)); }
public async Task ExecuteAsync(bool saveCdkProject) { // Ensure a .NET project can be found. ProjectDefinition project = null; try { project = new ProjectDefinition(_session.ProjectPath); } catch (ProjectFileNotFoundException ex) { var files = Directory.GetFiles(_session.ProjectPath, "*.sln"); if (files.Any()) { _toolInteractiveService.WriteErrorLine($"This directory contains a solution file, but the tool requires a project file. Please run the tool from the directory that contains a .csproj/.fsproj or provide a path to the .csproj/.fsproj via --project-path flag."); } else { _toolInteractiveService.WriteErrorLine($"A project was not found at the path {_session.ProjectPath}"); } throw new FailedToFindDeployableTargetException(ex); } var orchestrator = new Orchestrator.Orchestrator( _session, _orchestratorInteractiveService, _cdkProjectHandler, _awsResourceQueryer, _deploymentBundleHandler, new[] { RecipeLocator.FindRecipeDefinitionsPath() }); // Determine what recommendations are possible for the project. var recommendations = await orchestrator.GenerateDeploymentRecommendations(); if (recommendations.Count == 0) { _toolInteractiveService.WriteLine(string.Empty); _toolInteractiveService.WriteErrorLine($"The project you are trying to deploy is currently not supported."); throw new FailedToGenerateAnyRecommendations(); } // Look to see if there are any existing deployed applications using any of the compatible recommendations. var existingApplications = await orchestrator.GetExistingDeployedApplications(recommendations); _toolInteractiveService.WriteLine(string.Empty); string cloudApplicationName; if (existingApplications.Count == 0) { var title = "Name the AWS stack to deploy your application to" + Environment.NewLine + "(A stack is a collection of AWS resources that you can manage as a single unit.)" + Environment.NewLine + "--------------------------------------------------------------------------------"; cloudApplicationName = _consoleUtilities.AskUserForValue( title, GetDefaultApplicationName(project.ProjectPath), allowEmpty: false); } else { var title = "Select the AWS stack to deploy your application to" + Environment.NewLine + "(A stack is a collection of AWS resources that you can manage as a single unit.)"; var userResponse = _consoleUtilities.AskUserToChooseOrCreateNew(existingApplications.Select(x => x.Name).ToList(), title, askNewName: true, defaultNewName: GetDefaultApplicationName(project.ProjectPath)); cloudApplicationName = userResponse.SelectedOption ?? userResponse.NewName; } var existingCloudApplication = existingApplications.FirstOrDefault(x => string.Equals(x.Name, cloudApplicationName)); Recommendation selectedRecommendation = null; _toolInteractiveService.WriteLine(string.Empty); // If using a previous deployment preset settings for deployment based on last deployment. if (existingCloudApplication != null) { var existingCloudApplicationMetadata = await orchestrator.LoadCloudApplicationMetadata(existingCloudApplication.Name); selectedRecommendation = recommendations.FirstOrDefault(x => string.Equals(x.Recipe.Id, existingCloudApplication.RecipeId, StringComparison.InvariantCultureIgnoreCase)); selectedRecommendation.ApplyPreviousSettings(existingCloudApplicationMetadata.Settings); var header = $"Loading {existingCloudApplication.Name} settings:"; _toolInteractiveService.WriteLine(header); _toolInteractiveService.WriteLine(new string('-', header.Length)); var optionSettings = selectedRecommendation .Recipe .OptionSettings .Where(x => { if (!selectedRecommendation.IsOptionSettingDisplayable(x)) { return(false); } var value = selectedRecommendation.GetOptionSettingValue(x); if (value == null || value.ToString() == string.Empty || object.Equals(value, x.DefaultValue)) { return(false); } return(true); }) .ToArray(); foreach (var setting in optionSettings) { DisplayOptionSetting(selectedRecommendation, setting, -1, optionSettings.Length, DisplayOptionSettingsMode.Readonly); } } else { selectedRecommendation = _consoleUtilities.AskToChooseRecommendation(recommendations); } // Apply the user enter project name to the recommendation so that any default settings based on project name are applied. selectedRecommendation.OverrideProjectName(cloudApplicationName); var systemCapabilities = await _session.SystemCapabilities; if (selectedRecommendation.Recipe.DeploymentType == DeploymentTypes.CdkProject && !systemCapabilities.NodeJsMinVersionInstalled) { _toolInteractiveService.WriteErrorLine("The selected deployment option requires Node.js 10.3 or later, which was not detected. Please install Node.js: https://nodejs.org/en/download/"); throw new MissingNodeJsException(); } if (selectedRecommendation.Recipe.DeploymentBundle == DeploymentBundleTypes.Container) { if (!systemCapabilities.DockerInfo.DockerInstalled) { _toolInteractiveService.WriteErrorLine("The selected deployment option requires Docker, which was not detected. Please install and start the appropriate version of Docker for you OS: https://docs.docker.com/engine/install/"); throw new MissingDockerException(); } if (!systemCapabilities.DockerInfo.DockerContainerType.Equals("linux", StringComparison.OrdinalIgnoreCase)) { _toolInteractiveService.WriteErrorLine("The deployment tool requires Docker to be running in linux mode. Please switch Docker to linux mode to continue."); throw new DockerContainerTypeException(); } } var deploymentBundleDefinition = orchestrator.GetDeploymentBundleDefinition(selectedRecommendation); var configurableOptionSettings = selectedRecommendation.Recipe.OptionSettings.Union(deploymentBundleDefinition.Parameters); await ConfigureDeployment(selectedRecommendation, configurableOptionSettings, false); var cloudApplication = new CloudApplication { Name = cloudApplicationName }; if (!ConfirmDeployment(selectedRecommendation)) { return; } await CreateDeploymentBundle(orchestrator, selectedRecommendation, cloudApplication); await orchestrator.DeployRecommendation(cloudApplication, selectedRecommendation); }
/// <summary> /// Initiates a deployment or a re-deployment. /// If a new Cloudformation stack name is selected, then a fresh deployment is initiated with the user-selected deployment recipe. /// If an existing Cloudformation stack name is selected, then a re-deployment is initiated with the same deployment recipe. /// </summary> /// <param name="stackName">The stack name provided via the --stack-name CLI argument</param> /// <param name="userDeploymentSettings">The deserialized object from the user provided config file.<see cref="UserDeploymentSettings"/></param> /// <param name="deploymentProjectPath">The absolute or relative path of the CDK project that will be used for deployment</param> /// <returns>A tuple consisting of the Orchestrator object, Selected Recommendation, Cloud Application metadata.</returns> public async Task <(Orchestrator, Recommendation, CloudApplication)> InitializeDeployment(string stackName, UserDeploymentSettings?userDeploymentSettings, string deploymentProjectPath) { var orchestrator = new Orchestrator( _session, _orchestratorInteractiveService, _cdkProjectHandler, _cdkManager, _cdkVersionDetector, _awsResourceQueryer, _deploymentBundleHandler, _localUserSettingsEngine, _dockerEngine, _customRecipeLocator, new List <string> { RecipeLocator.FindRecipeDefinitionsPath() }, _directoryManager); // Determine what recommendations are possible for the project. var recommendations = await GenerateDeploymentRecommendations(orchestrator, deploymentProjectPath); // Get all existing applications that were previously deployed using our deploy tool. var allDeployedApplications = await _deployedApplicationQueryer.GetExistingDeployedApplications(); // Filter compatible applications that can be re-deployed using the current set of recommendations. var compatibleApplications = await _deployedApplicationQueryer.GetCompatibleApplications(recommendations, allDeployedApplications, _session); // Get Cloudformation stack name. var cloudApplicationName = GetCloudApplicationName(stackName, userDeploymentSettings, compatibleApplications); // Find existing application with the same CloudFormation stack name. var deployedApplication = allDeployedApplications.FirstOrDefault(x => string.Equals(x.Name, cloudApplicationName)); Recommendation?selectedRecommendation = null; if (deployedApplication != null) { // Verify that the target application can be deployed using the current set of recommendations if (!compatibleApplications.Any(app => app.StackName.Equals(deployedApplication.StackName, StringComparison.Ordinal))) { var errorMessage = $"{deployedApplication.StackName} already exists as a Cloudformation stack but a compatible recommendation to perform a redeployment was no found"; throw new FailedToFindCompatibleRecipeException(errorMessage); } // preset settings for deployment based on last deployment. selectedRecommendation = await GetSelectedRecommendationFromPreviousDeployment(recommendations, deployedApplication, userDeploymentSettings); } else { if (!string.IsNullOrEmpty(deploymentProjectPath)) { selectedRecommendation = recommendations.First(); } else { selectedRecommendation = GetSelectedRecommendation(userDeploymentSettings, recommendations); } } // Apply the user entered stack name to the recommendation so that any default settings based on stack name are applied. selectedRecommendation.AddReplacementToken(REPLACE_TOKEN_STACK_NAME, cloudApplicationName); var cloudApplication = new CloudApplication(cloudApplicationName, selectedRecommendation.Recipe.Id); return(orchestrator, selectedRecommendation, cloudApplication); }