Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow users to override the default deploy tool workspace directory #617

Merged
merged 1 commit into from
Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ nav:
- Pre-requisites: docs/getting-started/pre-requisites.md
- How to install: docs/getting-started/installation.md
- Set up Credentials: docs/getting-started/setup-creds.md
- Set up custom workspace: docs/getting-started/custom-workspace.md
- Deploy "Hello World": docs/getting-started/run-tool.md
- Support matrix: docs/support.md
# - Supported deployments:
Expand Down
11 changes: 11 additions & 0 deletions site/content/docs/getting-started/custom-workspace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
The default workspace used by AWS.Deploy.Tools is `$USERPROFILE/.aws-dotnet-deploy` on Windows and `$USER/.aws-dotnet-deploy` on Unix based OS. This workspace is used to create the CDK project and any other temporary files used by the tool.

You can override the default workspace by the setting the `AWS_DOTNET_DEPLOYTOOL_WORKSPACE` environment variable.

It must satisfy the following constraints:

* It must point to a valid directory that exists on the disk.
* The directory path must not have any whitespace characters in it.

**Setting up a custom workspace is optional for most users.** However, on Windows OS, if the `$USERPROFILE` path contains a whitespace character then the deployment will fail.
In that case, users are required to set up a custom workspace that satisfies the above constraints.
8 changes: 7 additions & 1 deletion site/content/troubleshooting-guide/other-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,10 @@ AWS.Deploy.Tools, internally uses a variety of different services to host your .
**Resolution**: You can refer to the official AWS documentation on IAM policies:

* See [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_managed-policies.html) for a tutorial on how to create customer managed IAM policies.
* See [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_policies.html) for troubleshooting IAM policies.
* See [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_policies.html) for troubleshooting IAM policies.

## Deployment failure due to whitespace character in USERPROFILE path

**Why is this happening**: This happens due to a know issue with the AWS Cloud Development Kit (CDK). The CDK is used to AWS.Deploy.Tools under the covers and it cannot cannot access the `$TEMP` directory inside the `$USERPROFILE` path if it contains a whitespace character.

**Resolution**: See [here](../../docs/getting-started/custom-workspace) for guidance on setting a custom workspace that will be used by AWS.Deploy.tools.
9 changes: 7 additions & 2 deletions src/AWS.Deploy.CLI/Commands/CommandFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public class CommandFactory : ICommandFactory
private readonly IOptionSettingHandler _optionSettingHandler;
private readonly IValidatorFactory _validatorFactory;
private readonly IRecipeHandler _recipeHandler;
private readonly IDeployToolWorkspaceMetadata _deployToolWorkspaceMetadata;

public CommandFactory(
IServiceProvider serviceProvider,
Expand Down Expand Up @@ -105,7 +106,9 @@ public CommandFactory(
IAWSServiceHandler awsServiceHandler,
IOptionSettingHandler optionSettingHandler,
IValidatorFactory validatorFactory,
IRecipeHandler recipeHandler)
IRecipeHandler recipeHandler,
IDeployToolWorkspaceMetadata deployToolWorkspaceMetadata
)
{
_serviceProvider = serviceProvider;
_toolInteractiveService = toolInteractiveService;
Expand Down Expand Up @@ -134,6 +137,7 @@ public CommandFactory(
_optionSettingHandler = optionSettingHandler;
_validatorFactory = validatorFactory;
_recipeHandler = recipeHandler;
_deployToolWorkspaceMetadata = deployToolWorkspaceMetadata;
}

public Command BuildRootCommand()
Expand Down Expand Up @@ -236,7 +240,8 @@ private Command BuildDeployCommand()
_awsServiceHandler,
_optionSettingHandler,
_validatorFactory,
_recipeHandler);
_recipeHandler,
_deployToolWorkspaceMetadata);

var deploymentProjectPath = input.DeploymentProject ?? string.Empty;
if (!string.IsNullOrEmpty(deploymentProjectPath))
Expand Down
8 changes: 6 additions & 2 deletions src/AWS.Deploy.CLI/Commands/DeployCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class DeployCommand
private readonly IOptionSettingHandler _optionSettingHandler;
private readonly IValidatorFactory _validatorFactory;
private readonly IRecipeHandler _recipeHandler;
private readonly IDeployToolWorkspaceMetadata _deployToolWorkspaceMetadata;

public DeployCommand(
IServiceProvider serviceProvider,
Expand All @@ -79,7 +80,8 @@ public DeployCommand(
IAWSServiceHandler awsServiceHandler,
IOptionSettingHandler optionSettingHandler,
IValidatorFactory validatorFactory,
IRecipeHandler recipeHandler)
IRecipeHandler recipeHandler,
IDeployToolWorkspaceMetadata deployToolWorkspaceMetadata)
{
_serviceProvider = serviceProvider;
_toolInteractiveService = toolInteractiveService;
Expand All @@ -105,6 +107,7 @@ public DeployCommand(
_optionSettingHandler = optionSettingHandler;
_validatorFactory = validatorFactory;
_recipeHandler = recipeHandler;
_deployToolWorkspaceMetadata = deployToolWorkspaceMetadata;
}

public async Task ExecuteAsync(string applicationName, string deploymentProjectPath, UserDeploymentSettings? userDeploymentSettings = null)
Expand Down Expand Up @@ -170,7 +173,8 @@ private void DisplayOutputResources(List<DisplayedResourceItem> displayedResourc
_fileManager,
_directoryManager,
_awsServiceHandler,
_optionSettingHandler);
_optionSettingHandler,
_deployToolWorkspaceMetadata);

// Determine what recommendations are possible for the project.
var recommendations = await GenerateDeploymentRecommendations(orchestrator, deploymentProjectPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public static void AddCustomServices(this IServiceCollection serviceCollection,
serviceCollection.TryAdd(new ServiceDescriptor(typeof(IOptionSettingHandler), typeof(OptionSettingHandler), lifetime));
serviceCollection.TryAdd(new ServiceDescriptor(typeof(IValidatorFactory), typeof(ValidatorFactory), lifetime));
serviceCollection.TryAdd(new ServiceDescriptor(typeof(IRecipeHandler), typeof(RecipeHandler), lifetime));
serviceCollection.TryAdd(new ServiceDescriptor(typeof(IEnvironmentVariableManager), typeof(EnvironmentVariableManager), lifetime));
serviceCollection.TryAdd(new ServiceDescriptor(typeof(IDeployToolWorkspaceMetadata), typeof(DeployToolWorkspaceMetadata), lifetime));

var packageJsonTemplate = typeof(PackageJsonGenerator).Assembly.ReadEmbeddedFile(PackageJsonGenerator.TemplateIdentifier);
serviceCollection.TryAdd(new ServiceDescriptor(typeof(IPackageJsonGenerator), (serviceProvider) => new PackageJsonGenerator(packageJsonTemplate), lifetime));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,8 @@ private CdkProjectHandler CreateCdkProjectHandler(SessionState state, IServicePr
serviceProvider.GetRequiredService<ICommandLineWrapper>(),
serviceProvider.GetRequiredService<IAWSResourceQueryer>(),
serviceProvider.GetRequiredService<IFileManager>(),
serviceProvider.GetRequiredService<IOptionSettingHandler>()
serviceProvider.GetRequiredService<IOptionSettingHandler>(),
serviceProvider.GetRequiredService<IDeployToolWorkspaceMetadata>()
);
}

Expand Down Expand Up @@ -754,7 +755,8 @@ private Orchestrator CreateOrchestrator(SessionState state, IServiceProvider? se
serviceProvider.GetRequiredService<IFileManager>(),
serviceProvider.GetRequiredService<IDirectoryManager>(),
serviceProvider.GetRequiredService<IAWSServiceHandler>(),
serviceProvider.GetRequiredService<IOptionSettingHandler>()
serviceProvider.GetRequiredService<IOptionSettingHandler>(),
serviceProvider.GetRequiredService<IDeployToolWorkspaceMetadata>()
);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/AWS.Deploy.Common/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ public enum DeployToolErrorCode
InvalidCloudApplicationName = 10009500,
SelectedValueIsNotAllowed = 10009600,
MissingValidatorConfiguration = 10009700,
InvalidFilePath = 10009800
InvalidFilePath = 10009800,
InvalidDeployToolWorkspace = 10009900
}

public class ProjectFileNotFoundException : DeployToolException
Expand Down
23 changes: 0 additions & 23 deletions src/AWS.Deploy.Constants/CDK.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,11 @@ namespace AWS.Deploy.Constants
{
internal static class CDK
{
/// <summary>
/// Deployment tool workspace directory to create CDK app during the deployment.
/// </summary>
public static string DeployToolWorkspaceDirectoryRoot {
get
{
var deployToolWorkspace = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".aws-dotnet-deploy");
if (!Directory.Exists(deployToolWorkspace))
Directory.CreateDirectory(deployToolWorkspace);
return deployToolWorkspace;
}
}

/// <summary>
/// Directory that contains CDK projects
/// </summary>
public static string ProjectsDirectory => Path.Combine(DeployToolWorkspaceDirectoryRoot, "Projects");

/// <summary>
/// Default version of CDK CLI
/// </summary>
public static readonly Version DefaultCDKVersion = Version.Parse("2.13.0");

/// <summary>
/// The file path of the CDK bootstrap template to be used
/// </summary>
public static string CDKBootstrapTemplatePath => Path.Combine(DeployToolWorkspaceDirectoryRoot, "CDKBootstrapTemplate.yaml");

/// <summary>
/// The version number CDK bootstrap specified in CDKBootstrapTemplate.yaml
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/AWS.Deploy.Constants/CLI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ internal static class CLI
public const string PROMPT_CHOOSE_DEPLOYMENT_TARGET = "Choose deployment target";

public const string CLI_APP_NAME = "AWS .NET Deployment Tool";
public const string WORKSPACE_ENV_VARIABLE = "AWS_DOTNET_DEPLOYTOOL_WORKSPACE";
}
}
6 changes: 4 additions & 2 deletions src/AWS.Deploy.Orchestration/CDK/CDKManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,24 @@ public class CDKManager : ICDKManager
private readonly ICDKInstaller _cdkInstaller;
private readonly INPMPackageInitializer _npmPackageInitializer;
private readonly IOrchestratorInteractiveService _interactiveService;
private readonly IDeployToolWorkspaceMetadata _workspaceMetadata;

private const string TemplateIdentifier = "AWS.Deploy.Orchestration.CDK.CDKBootstrapTemplate.yaml";

public CDKManager(ICDKInstaller cdkInstaller, INPMPackageInitializer npmPackageInitializer, IOrchestratorInteractiveService interactiveService)
public CDKManager(ICDKInstaller cdkInstaller, INPMPackageInitializer npmPackageInitializer, IOrchestratorInteractiveService interactiveService, IDeployToolWorkspaceMetadata workspaceMetadata)
{
_cdkInstaller = cdkInstaller;
_npmPackageInitializer = npmPackageInitializer;
_interactiveService = interactiveService;
_workspaceMetadata = workspaceMetadata;
}

private async Task CreateCDKBootstrapTemplate()
{
// The CDK bootstrap template can be generated by running 'cdk bootstrap --show-template'.
// We need to keep the template up to date while making sure that the 'Staging Bucket' retention policies are set to 'Delete'.
var cdkBootstrapTemplate = typeof(CdkProjectHandler).Assembly.ReadEmbeddedFile(TemplateIdentifier);
await using var cdkBootstrapTemplateFile = new StreamWriter(Constants.CDK.CDKBootstrapTemplatePath);
await using var cdkBootstrapTemplateFile = new StreamWriter(_workspaceMetadata.CDKBootstrapTemplatePath);
await cdkBootstrapTemplateFile.WriteAsync(cdkBootstrapTemplate);
}

Expand Down
13 changes: 8 additions & 5 deletions src/AWS.Deploy.Orchestration/CdkProjectHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,23 @@ public class CdkProjectHandler : ICdkProjectHandler
private readonly IDirectoryManager _directoryManager;
private readonly IAWSResourceQueryer _awsResourceQueryer;
private readonly IFileManager _fileManager;
private readonly IDeployToolWorkspaceMetadata _workspaceMetadata;

public CdkProjectHandler(
IOrchestratorInteractiveService interactiveService,
ICommandLineWrapper commandLineWrapper,
IAWSResourceQueryer awsResourceQueryer,
IFileManager fileManager,
IOptionSettingHandler optionSettingHandler)
IOptionSettingHandler optionSettingHandler,
IDeployToolWorkspaceMetadata workspaceMetadata)
{
_interactiveService = interactiveService;
_commandLineWrapper = commandLineWrapper;
_awsResourceQueryer = awsResourceQueryer;
_appSettingsBuilder = new CdkAppSettingsSerializer(optionSettingHandler);
_directoryManager = new DirectoryManager();
_fileManager = fileManager;
_workspaceMetadata = workspaceMetadata;
}

public async Task<string> ConfigureCdkProject(OrchestratorSession session, CloudApplication cloudApplication, Recommendation recommendation)
Expand Down Expand Up @@ -110,7 +113,7 @@ public async Task DeployCdkProject(OrchestratorSession session, CloudApplication
if (await DetermineIfCDKBootstrapShouldRun())
{
// Ensure region is bootstrapped
var cdkBootstrap = await _commandLineWrapper.TryRunWithResult($"npx cdk bootstrap aws://{session.AWSAccountId}/{session.AWSRegion} -c {Constants.CloudFormationIdentifier.SETTINGS_PATH_CDK_CONTEXT_PARAMETER}=\"{appSettingsFilePath}\" --template \"{Constants.CDK.CDKBootstrapTemplatePath}\"",
var cdkBootstrap = await _commandLineWrapper.TryRunWithResult($"npx cdk bootstrap aws://{session.AWSAccountId}/{session.AWSRegion} -c {Constants.CloudFormationIdentifier.SETTINGS_PATH_CDK_CONTEXT_PARAMETER}=\"{appSettingsFilePath}\" --template \"{_workspaceMetadata.CDKBootstrapTemplatePath}\"",
workingDirectory: cdkProjectPath,
needAwsCredentials: true,
redirectIO: true,
Expand Down Expand Up @@ -203,7 +206,7 @@ public async Task<bool> DetermineIfCDKBootstrapShouldRun()
private async Task<string> RetrieveStackId(CloudApplication cloudApplication, CancellationToken cancellationToken)
{
Stack? stack = null;
await WaitUntilHelper.WaitUntil(async () =>
await Helpers.WaitUntil(async () =>
{
try
{
Expand Down Expand Up @@ -256,7 +259,7 @@ public string CreateCdkProject(Recommendation recommendation, OrchestratorSessio
{
saveCdkDirectoryPath =
Path.Combine(
Constants.CDK.ProjectsDirectory,
_workspaceMetadata.ProjectsDirectory,
Path.GetFileNameWithoutExtension(Path.GetRandomFileName()));

assemblyName = recommendation.ProjectDefinition.AssemblyName;
Expand All @@ -280,7 +283,7 @@ public string CreateCdkProject(Recommendation recommendation, OrchestratorSessio

public void DeleteTemporaryCdkProject(string cdkProjectPath)
{
var parentPath = Path.GetFullPath(Constants.CDK.ProjectsDirectory);
var parentPath = Path.GetFullPath(_workspaceMetadata.ProjectsDirectory);
cdkProjectPath = Path.GetFullPath(cdkProjectPath);

if (!cdkProjectPath.StartsWith(parentPath))
Expand Down
56 changes: 56 additions & 0 deletions src/AWS.Deploy.Orchestration/DeployToolWorkspaceMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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.Text;
using AWS.Deploy.Common.IO;
using AWS.Deploy.Orchestration.Utilities;

namespace AWS.Deploy.Orchestration
{
public interface IDeployToolWorkspaceMetadata
{
/// <summary>
/// Deployment tool workspace directory to create CDK app during the deployment.
/// </summary>
string DeployToolWorkspaceDirectoryRoot { get; }

/// <summary>
/// Directory that contains CDK projects
/// </summary>
string ProjectsDirectory { get; }

/// <summary>
/// The file path of the CDK bootstrap template to be used
/// </summary>
string CDKBootstrapTemplatePath { get; }
}

public class DeployToolWorkspaceMetadata : IDeployToolWorkspaceMetadata
{
private readonly IDirectoryManager _directoryManager;
private readonly IEnvironmentVariableManager _environmentVariableManager;

public string DeployToolWorkspaceDirectoryRoot
{
get
{
var workspace = Helpers.GetDeployToolWorkspaceDirectoryRoot(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), _directoryManager, _environmentVariableManager);
if (!_directoryManager.Exists(workspace))
_directoryManager.CreateDirectory(workspace);
return workspace;
}
}

public string ProjectsDirectory => Path.Combine(DeployToolWorkspaceDirectoryRoot, "Projects");
public string CDKBootstrapTemplatePath => Path.Combine(DeployToolWorkspaceDirectoryRoot, "CDKBootstrapTemplate.yaml");

public DeployToolWorkspaceMetadata(IDirectoryManager directoryManager, IEnvironmentVariableManager environmentVariableManager)
{
_directoryManager = directoryManager;
_environmentVariableManager = environmentVariableManager;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public async Task ExecuteAsync(Orchestrator orchestrator, CloudApplication cloud
throw new InvalidOperationException($"{nameof(orchestrator._cdkVersionDetector)} must not be null.");
if (orchestrator._directoryManager == null)
throw new InvalidOperationException($"{nameof(orchestrator._directoryManager)} must not be null.");
if (orchestrator._workspaceMetadata == null)
throw new InvalidOperationException($"{nameof(orchestrator._workspaceMetadata)} must not be null.");

orchestrator._interactiveService.LogSectionStart("Configuring AWS Cloud Development Kit (CDK)",
"Ensure a compatible CDK version is installed and the CDK bootstrap CloudFormation stack has been created. A CDK project to perform the deployment is generated unless an existing deployment project was selected.");
Expand All @@ -38,7 +40,7 @@ public async Task ExecuteAsync(Orchestrator orchestrator, CloudApplication cloud
var projFiles = orchestrator._directoryManager.GetProjFiles(cdkProject);
var cdkVersion = orchestrator._cdkVersionDetector.Detect(projFiles);

await orchestrator._cdkManager.EnsureCompatibleCDKExists(recommendation.Recipe.PersistedDeploymentProject ? cdkProject : Constants.CDK.DeployToolWorkspaceDirectoryRoot, cdkVersion);
await orchestrator._cdkManager.EnsureCompatibleCDKExists(recommendation.Recipe.PersistedDeploymentProject ? cdkProject : orchestrator._workspaceMetadata.DeployToolWorkspaceDirectoryRoot, cdkVersion);

try
{
Expand Down
Loading