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);
        }
Exemplo n.º 4
0
        /// <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);
        }