private async Task CreateContainerDeploymentBundle()

in src/AWS.Deploy.Orchestration/Orchestrator.cs [292:374]


        private async Task CreateContainerDeploymentBundle(CloudApplication cloudApplication, Recommendation recommendation)
        {
            if (_interactiveService == null)
                throw new InvalidOperationException($"{nameof(_interactiveService)} is null as part of the orchestartor object");
            if (_dockerEngine == null)
                throw new InvalidOperationException($"{nameof(_dockerEngine)} is null as part of the orchestartor object");
            if (_deploymentBundleHandler == null)
                throw new InvalidOperationException($"{nameof(_deploymentBundleHandler)} is null as part of the orchestrator object");
            if (_optionSettingHandler == null)
                throw new InvalidOperationException($"{nameof(_optionSettingHandler)} is null as part of the orchestrator object");
            if (_fileManager == null)
                throw new InvalidOperationException($"{nameof(_fileManager)} is null as part of the orchestrator object");

            if (!DockerUtilities.TryGetDockerfile(recommendation, _fileManager, out _))
            {
                _interactiveService.LogInfoMessage("Generating Dockerfile...");
                try
                {
                    _dockerEngine.GenerateDockerFile(recommendation);
                }
                catch (DockerEngineExceptionBase ex)
                {
                    var errorMessage = "Failed to generate a docker file due to the following error:" + Environment.NewLine + ex.Message;
                    throw new FailedToGenerateDockerFileException(DeployToolErrorCode.FailedToGenerateDockerFile, errorMessage, ex);
                }
            }

            _dockerEngine.DetermineDockerExecutionDirectory(recommendation);

            // Read this from the OptionSetting instead of recommendation.DeploymentBundle.
            // When its value comes from a replacement token, it wouldn't have been set back to the DeploymentBundle
            var respositoryName = _optionSettingHandler.GetOptionSettingValue<string>(recommendation, _optionSettingHandler.GetOptionSetting(recommendation, Constants.Docker.ECRRepositoryNameOptionId));
            if (respositoryName == null)
                throw new InvalidECRRepositoryNameException(DeployToolErrorCode.ECRRepositoryNameIsNull, "The ECR Repository Name is null.");

            string imageTag;
            try
            {
                var tagSuffix = _optionSettingHandler.GetOptionSettingValue<string>(recommendation, _optionSettingHandler.GetOptionSetting(recommendation, Constants.Docker.ImageTagOptionId));
                imageTag = $"{respositoryName}:{tagSuffix}";
            }
            catch (OptionSettingItemDoesNotExistException)
            {
                imageTag = $"{respositoryName}:{DateTime.UtcNow.Ticks}";
            }

            await _deploymentBundleHandler.BuildDockerImage(cloudApplication, recommendation, imageTag);

            // These option settings need to be persisted back as they are not always provided by the user and we have custom logic to determine their values
            await _optionSettingHandler.SetOptionSettingValue(recommendation, Constants.Docker.DockerExecutionDirectoryOptionId, recommendation.DeploymentBundle.DockerExecutionDirectory);
            await _optionSettingHandler.SetOptionSettingValue(recommendation, Constants.Docker.DockerfileOptionId, recommendation.DeploymentBundle.DockerfilePath);

            // Try to inspect the container environment variables to provide better insights on the HTTP port to use for the container.
            // If we run into issues doing so, we can proceed without throwing a terminating exception.
            try
            {
                var environmentVariables = await _deploymentBundleHandler.InspectDockerImageEnvironmentVariables(recommendation, imageTag);

                if (environmentVariables.ContainsKey(Constants.Docker.DotnetHttpPortEnvironmentVariable))
                {
                    var httpPort = environmentVariables[Constants.Docker.DotnetHttpPortEnvironmentVariable];

                    // Assuming a single value can be specified
                    if (int.TryParse(httpPort, out var httpPortInt))
                    {
                        if (recommendation.DeploymentBundle.DockerfileHttpPort != httpPortInt)
                        {
                            _interactiveService.LogInfoMessage($"The HTTP port you have chosen in your deployment settings is different than the .NET HTTP port exposed in the container. " +
                                $"The container has the environment variable {Constants.Docker.DotnetHttpPortEnvironmentVariable}={httpPortInt}, " +
                                $"whereas the port you chose in the deployment settings is {recommendation.DeploymentBundle.DockerfileHttpPort}." +
                                $"The deployment may fail the health check if these 2 ports are misaligned.");
                        }
                    }
                }
            }
            catch (DockerInspectFailedException ex)
            {
                _interactiveService.LogDebugMessage($"Unable to inspect the docker container to retrieve the HTTP port used by .NET due to the following error: {ex.Message}");
            }

            _interactiveService.LogSectionStart("Pushing container image to Elastic Container Registry (ECR)", "Using the docker CLI to log on to ECR and push the local image to ECR.");
            await _deploymentBundleHandler.PushDockerImageToECR(recommendation, respositoryName, imageTag);
        }