// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using AWS.Deploy.Common;
using AWS.Deploy.Common.IO;
using AWS.Deploy.Common.Recipes;
namespace AWS.Deploy.Orchestration.Utilities
{
///
/// This class holds static helper methods
///
public static class Helpers
{
///
/// Wait for TimeSpan until isn't satisfied
///
/// Termination condition for breaking the wait loop
/// Interval between the two executions of the task
/// Interval for timeout, if timeout passes, methods throws
/// Throws when timeout passes
public static async Task WaitUntil(Func> predicate, TimeSpan frequency, TimeSpan timeout, CancellationToken cancellationToken = default)
{
var waitTask = Task.Run(async () =>
{
while (!cancellationToken.IsCancellationRequested && !await predicate())
{
await Task.Delay(frequency);
}
});
if (waitTask != await Task.WhenAny(waitTask, Task.Delay(timeout)))
{
throw new TimeoutException();
}
}
///
/// This method returns the deployment tool workspace directory to create the CDK app during deployment.
/// It will first look for AWS_DOTNET_DEPLOYTOOL_WORKSPACE environment variable set by the user. It will be used as the deploy tool workspace if it points to a valid directory whithout whitespace characters in its path.
/// If the environment variable is set, it will also create a temp directory inside the workspace and set process scoped TEMP and TMP environment variables that point to the temp directory.
/// This additional configuration is required due to a known issue in the CDK - https://github.com/aws/aws-cdk/issues/2532
/// If the override is not present, then it defaults to USERPROFILE/.aws-dotnet-deploy.
/// It will throw an exception if the USERPROFILE contains a whitespace character.
///
public static string GetDeployToolWorkspaceDirectoryRoot(string userProfilePath, IDirectoryManager directoryManager, IEnvironmentVariableManager environmentVariableManager)
{
var overridenWorkspace = environmentVariableManager.GetEnvironmentVariable(Constants.CLI.WORKSPACE_ENV_VARIABLE);
var defaultWorkSpace = Path.Combine(userProfilePath, ".aws-dotnet-deploy");
if (overridenWorkspace == null)
{
return !defaultWorkSpace.Contains(" ")
? defaultWorkSpace
: throw new InvalidDeployToolWorkspaceException(DeployToolErrorCode.InvalidDeployToolWorkspace, $"The USERPROFILE path ({userProfilePath}) contains a whitespace character and cannot be used as a workspace by the deployment tool. " +
$"Please refer to the troubleshooting guide for setting the {Constants.CLI.WORKSPACE_ENV_VARIABLE} that specifies an alternative workspace directory.");
}
if (!directoryManager.Exists(overridenWorkspace))
{
throw new InvalidDeployToolWorkspaceException(DeployToolErrorCode.InvalidDeployToolWorkspace,
$"The {Constants.CLI.WORKSPACE_ENV_VARIABLE} environment variable has been set to \"{overridenWorkspace}\" but it does not point to a valid directory.");
}
if (overridenWorkspace.Contains(" "))
{
throw new InvalidDeployToolWorkspaceException(DeployToolErrorCode.InvalidDeployToolWorkspace,
$"The {Constants.CLI.WORKSPACE_ENV_VARIABLE} environment variable ({overridenWorkspace}) contains a whitespace character and cannot be used as a workspace by the deployment tool.");
}
var tempDir = Path.Combine(overridenWorkspace, "temp");
if (!directoryManager.Exists(tempDir))
{
directoryManager.CreateDirectory(tempDir);
}
// This is done to override the C:\Users\\AppData\Local\Temp directory.
// There is a known issue with CDK where it cannot access the Temp directory if the username contains a whitespace.
environmentVariableManager.SetEnvironmentVariable("TMP", tempDir);
environmentVariableManager.SetEnvironmentVariable("TEMP", tempDir);
return overridenWorkspace;
}
///
/// Creates a
///
/// Absolute or relative JSON file path where the deployment settings will be saved. Only the settings modified by the user are persisted.
/// Absolute or relative JSON file path where the deployment settings will be saved. All deployment settings are persisted.
/// Absolute path to the user's .NET project directory
///
///
public static SaveSettingsConfiguration GetSaveSettingsConfiguration(string? saveSettingsPath, string? saveAllSettingsPath, string projectDirectoryPath, IFileManager fileManager)
{
if (!string.IsNullOrEmpty(saveSettingsPath) && !string.IsNullOrEmpty(saveAllSettingsPath))
{
throw new FailedToSaveDeploymentSettingsException(DeployToolErrorCode.FailedToSaveDeploymentSettings, "Cannot save deployment settings because invalid arguments were provided. Cannot use --save-settings along with --save-all-settings");
}
var filePath = string.Empty;
var saveSettingsType = SaveSettingsType.None;
if (!string.IsNullOrEmpty(saveSettingsPath))
{
filePath = saveSettingsPath;
saveSettingsType = SaveSettingsType.Modified;
}
else if (!string.IsNullOrEmpty(saveAllSettingsPath))
{
filePath = saveAllSettingsPath;
saveSettingsType = SaveSettingsType.All;
}
if (!string.IsNullOrEmpty(filePath))
{
filePath = Path.GetFullPath(filePath, projectDirectoryPath);
if (!fileManager.IsFileValidPath(filePath))
{
var message = $"Failed to save deployment settings because {filePath} is invalid or its parent directory does not exist on disk.";
throw new FailedToSaveDeploymentSettingsException(DeployToolErrorCode.FailedToSaveDeploymentSettings, message);
}
}
return new SaveSettingsConfiguration(saveSettingsType, filePath);
}
}
}