Skip to content

Commit

Permalink
Merge pull request #264 from aws/kmalhar/create-deployment-manifest-file
Browse files Browse the repository at this point in the history
feat: Create a deployment-manifest file
  • Loading branch information
96malhar authored Jul 26, 2021
2 parents fd65f98 + b179c20 commit 1a9cc33
Show file tree
Hide file tree
Showing 12 changed files with 329 additions and 13 deletions.
8 changes: 6 additions & 2 deletions src/AWS.Deploy.CLI/Commands/CommandFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using AWS.Deploy.Orchestration.Utilities;
using AWS.Deploy.CLI.Commands.CommandHandlerInput;
using AWS.Deploy.Common.IO;
using AWS.Deploy.Common.DeploymentManifest;

namespace AWS.Deploy.CLI.Commands
{
Expand Down Expand Up @@ -52,6 +53,7 @@ public class CommandFactory : ICommandFactory
private readonly IDeployedApplicationQueryer _deployedApplicationQueryer;
private readonly ITypeHintCommandFactory _typeHintCommandFactory;
private readonly IConsoleUtilities _consoleUtilities;
private readonly IDeploymentManifestEngine _deploymentManifestEngine;

public CommandFactory(
IToolInteractiveService toolInteractiveService,
Expand All @@ -69,7 +71,8 @@ public CommandFactory(
ITemplateMetadataReader templateMetadataReader,
IDeployedApplicationQueryer deployedApplicationQueryer,
ITypeHintCommandFactory typeHintCommandFactory,
IConsoleUtilities consoleUtilities)
IConsoleUtilities consoleUtilities,
IDeploymentManifestEngine deploymentManifestEngine)
{
_toolInteractiveService = toolInteractiveService;
_orchestratorInteractiveService = orchestratorInteractiveService;
Expand All @@ -87,6 +90,7 @@ public CommandFactory(
_deployedApplicationQueryer = deployedApplicationQueryer;
_typeHintCommandFactory = typeHintCommandFactory;
_consoleUtilities = consoleUtilities;
_deploymentManifestEngine = deploymentManifestEngine;
}

public Command BuildRootCommand()
Expand Down Expand Up @@ -352,6 +356,7 @@ private Command BuildDeploymentProjectCommand()
new DirectoryManager(),
new FileManager(),
session,
_deploymentManifestEngine,
targetApplicationFullPath);

await generateDeploymentProject.ExecuteAsync(saveDirectory, projectDisplayName);
Expand All @@ -360,7 +365,6 @@ private Command BuildDeploymentProjectCommand()
}
catch (Exception e) when (e.IsAWSDeploymentExpectedException())
{
_toolInteractiveService.WriteErrorLine("Failed to generate deployment project.");
if (input.Diagnostics)
_toolInteractiveService.WriteErrorLine(e.PrettyPrint());
else
Expand Down
13 changes: 10 additions & 3 deletions src/AWS.Deploy.CLI/Commands/GenerateDeploymentProjectCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Threading.Tasks;
using AWS.Deploy.Common;
using AWS.Deploy.Common.DeploymentManifest;
using AWS.Deploy.Common.IO;
using AWS.Deploy.Common.Recipes;
using AWS.Deploy.Orchestration;
Expand All @@ -28,6 +29,7 @@ public class GenerateDeploymentProjectCommand
private readonly IDirectoryManager _directoryManager;
private readonly IFileManager _fileManager;
private readonly OrchestratorSession _session;
private readonly IDeploymentManifestEngine _deploymentManifestEngine;
private readonly string _targetApplicationFullPath;

public GenerateDeploymentProjectCommand(
Expand All @@ -38,6 +40,7 @@ public GenerateDeploymentProjectCommand(
IDirectoryManager directoryManager,
IFileManager fileManager,
OrchestratorSession session,
IDeploymentManifestEngine deploymentManifestEngine,
string targetApplicationFullPath)
{
_toolInteractiveService = toolInteractiveService;
Expand All @@ -47,6 +50,7 @@ public GenerateDeploymentProjectCommand(
_directoryManager = directoryManager;
_fileManager = fileManager;
_session = session;
_deploymentManifestEngine = deploymentManifestEngine;
_targetApplicationFullPath = targetApplicationFullPath;
}

Expand All @@ -73,7 +77,8 @@ public async Task ExecuteAsync(string saveCdkDirectoryPath, string projectDispla
{
if (newDirectoryCreated)
_directoryManager.Delete(saveCdkDirectoryPath);
throw new InvalidSaveDirectoryForCdkProject(errorMessage);
errorMessage = $"Failed to generate deployment project.{Environment.NewLine}{errorMessage}";
throw new InvalidSaveDirectoryForCdkProject(errorMessage.Trim());
}

var directoryUnderSourceControl = await IsDirectoryUnderSourceControl(saveCdkDirectoryPath);
Expand All @@ -98,9 +103,11 @@ public async Task ExecuteAsync(string saveCdkDirectoryPath, string projectDispla
await _cdkProjectHandler.CreateCdkProjectForDeployment(selectedRecommendation, _session, saveCdkDirectoryPath);
await GenerateDeploymentRecipeSnapShot(selectedRecommendation, saveCdkDirectoryPath, projectDisplayName);

var saveCdkDirectoryInfo = _directoryManager.GetDirectoryInfo(saveCdkDirectoryPath);
var saveCdkDirectoryFullPath = _directoryManager.GetDirectoryInfo(saveCdkDirectoryPath).FullName;
_toolInteractiveService.WriteLine();
_toolInteractiveService.WriteLine($"The CDK deployment project is saved at: {saveCdkDirectoryInfo.FullName}");
_toolInteractiveService.WriteLine($"The CDK deployment project is saved at: {saveCdkDirectoryFullPath}");

await _deploymentManifestEngine.UpdateDeploymentManifestFile(saveCdkDirectoryFullPath, _targetApplicationFullPath);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using AWS.Deploy.CLI.Commands.TypeHints;
using AWS.Deploy.CLI.Utilities;
using AWS.Deploy.Common;
using AWS.Deploy.Common.DeploymentManifest;
using AWS.Deploy.Common.Extensions;
using AWS.Deploy.Common.IO;
using AWS.Deploy.Orchestration;
Expand Down Expand Up @@ -48,6 +49,7 @@ public static void AddCustomServices(this IServiceCollection serviceCollection,
serviceCollection.TryAdd(new ServiceDescriptor(typeof(IToolInteractiveService), typeof(ConsoleInteractiveServiceImpl), lifetime));
serviceCollection.TryAdd(new ServiceDescriptor(typeof(ITypeHintCommandFactory), typeof(TypeHintCommandFactory), lifetime));
serviceCollection.TryAdd(new ServiceDescriptor(typeof(IZipFileManager), typeof(ZipFileManager), lifetime));
serviceCollection.TryAdd(new ServiceDescriptor(typeof(IDeploymentManifestEngine), typeof(DeploymentManifestEngine), lifetime));
serviceCollection.TryAdd(new ServiceDescriptor(typeof(ICommandFactory), typeof(CommandFactory), lifetime));

var packageJsonTemplate = typeof(PackageJsonGenerator).Assembly.ReadEmbeddedFile(PackageJsonGenerator.TemplateIdentifier);
Expand Down
107 changes: 107 additions & 0 deletions src/AWS.Deploy.Common/DeploymentManifest/DeploymentManifestEngine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// 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.Threading.Tasks;
using AWS.Deploy.Common.IO;
using Newtonsoft.Json;

namespace AWS.Deploy.Common.DeploymentManifest
{
public interface IDeploymentManifestEngine
{
Task UpdateDeploymentManifestFile(string saveCdkDirectoryFullPath, string targetApplicationFullPath);
}

/// <summary>
/// This class contains the helper methods to update the deployment manifest file
/// that keeps track of the save CDK deployment projects.
/// </summary>
public class DeploymentManifestEngine : IDeploymentManifestEngine
{
private readonly IDirectoryManager _directoryManager;
private readonly IFileManager _fileManager;

private const string DEPLOYMENT_MANIFEST_FILE_NAME = "aws-deployments.json";

public DeploymentManifestEngine(IDirectoryManager directoryManager, IFileManager fileManager)
{
_directoryManager = directoryManager;
_fileManager = fileManager;
}

/// <summary>
/// This method updates the deployment manifest json file by adding the directory path at which the CDK deployment project is saved.
/// If the manifest file does not exists then a new file is generated.
/// <param name="saveCdkDirectoryFullPath">The absolute path to the directory at which the CDK deployment project is saved</param>
/// <param name="targetApplicationFullPath">The absolute path to the target application csproj or fsproj file.</param>
/// <exception cref="FailedToUpdateDeploymentManifestFileException">Thrown if an error occured while trying to update the deployment manifest file.</exception>
/// </summary>
/// <returns></returns>
public async Task UpdateDeploymentManifestFile(string saveCdkDirectoryFullPath, string targetApplicationFullPath)
{
try
{
var deploymentManifestFilePath = GetDeploymentManifestFilePath(targetApplicationFullPath);
var saveCdkDirectoryRelativePath = _directoryManager.GetRelativePath(targetApplicationFullPath, saveCdkDirectoryFullPath);

DeploymentManifestModel deploymentManifestModel;

if (_fileManager.Exists(deploymentManifestFilePath))
{
deploymentManifestModel = await ReadManifestFile(deploymentManifestFilePath);
deploymentManifestModel.DeploymentManifestEntries.Add(new DeploymentManifestEntry(saveCdkDirectoryRelativePath));
}
else
{
var deploymentManifestEntries = new List<DeploymentManifestEntry> { new DeploymentManifestEntry(saveCdkDirectoryRelativePath) };
deploymentManifestModel = new DeploymentManifestModel(deploymentManifestEntries);
}

var manifestFileJsonString = SerializeManifestModel(deploymentManifestModel);
await _fileManager.WriteAllTextAsync(deploymentManifestFilePath, manifestFileJsonString);
}
catch (Exception ex)
{
throw new FailedToUpdateDeploymentManifestFileException($"Failed to update the deployment manifest file " +
$"for the deployment project stored at '{saveCdkDirectoryFullPath}'", ex);
}

}

/// <summary>
/// This method parses the deployment-manifest file into a <see cref="DeploymentManifestModel"/>
/// </summary>
/// <param name="filePath">The path to the deployment-manifest file</param>
/// <returns>An instance of <see cref="DeploymentManifestModel"/></returns>
private async Task<DeploymentManifestModel> ReadManifestFile(string filePath)
{
var manifestFilejsonString = await _fileManager.ReadAllTextAsync(filePath);
return JsonConvert.DeserializeObject<DeploymentManifestModel>(manifestFilejsonString);
}

/// <summary>
/// This method parses the <see cref="DeploymentManifestModel"/> into a string
/// </summary>
/// <param name="deploymentManifestModel"><see cref="DeploymentManifestModel"/></param>
/// <returns>A formatted string representation of <see cref="DeploymentManifestModel"></returns>
private string SerializeManifestModel(DeploymentManifestModel deploymentManifestModel)
{
return JsonConvert.SerializeObject(deploymentManifestModel, Formatting.Indented);
}

/// <summary>
/// This method returns the path at which the deployment-manifest file will be stored.
/// <param name="targetApplicationFullPath">The absolute path to the target application csproj or fsproj file</param>
/// </summary>
/// <returns>The path to the deployment-manifest file.</returns>
private string GetDeploymentManifestFilePath(string targetApplicationFullPath)
{
var projectDirectoryFullPath = _directoryManager.GetDirectoryInfo(targetApplicationFullPath).Parent.FullName;
var deploymentManifestFileFullPath = Path.Combine(projectDirectoryFullPath, DEPLOYMENT_MANIFEST_FILE_NAME);
return deploymentManifestFileFullPath;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using Newtonsoft.Json;

namespace AWS.Deploy.Common.DeploymentManifest
{
/// <summary>
/// This class supports serialization and de-serialization of the deployment-manifest file.
/// </summary>
public class DeploymentManifestEntry
{
[JsonProperty("Path")]
public string SaveCdkDirectoryRelativePath { get; set; }

public DeploymentManifestEntry(string saveCdkDirectoryRelativePath)
{
SaveCdkDirectoryRelativePath = saveCdkDirectoryRelativePath;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using System.Collections.Generic;
using Newtonsoft.Json;

namespace AWS.Deploy.Common.DeploymentManifest
{
/// <summary>
/// This class supports serialization and de-serialization of the deployment-manifest file.
/// </summary>
public class DeploymentManifestModel
{
[JsonProperty("deployment-projects")]
public List<DeploymentManifestEntry> DeploymentManifestEntries { get; set; }

public DeploymentManifestModel(List<DeploymentManifestEntry> deploymentManifestEntries)
{
DeploymentManifestEntries = deploymentManifestEntries;
}
}
}
8 changes: 8 additions & 0 deletions src/AWS.Deploy.Common/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ public class NoDeploymentBundleDefinitionsFoundException : Exception
{
public NoDeploymentBundleDefinitionsFoundException(string message, Exception? innerException = null) : base(message, innerException) { }
}

/// Exception thrown if a failure occured while trying to update the deployment manifest file.
/// </summary>
[AWSDeploymentExpectedException]
public class FailedToUpdateDeploymentManifestFileException : Exception
{
public FailedToUpdateDeploymentManifestFileException(string message, Exception? innerException = null) : base(message, innerException) { }
}

/// <summary>
/// Indicates a specific strongly typed Exception can be anticipated.
Expand Down
3 changes: 3 additions & 0 deletions src/AWS.Deploy.Common/IO/DirectoryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public interface IDirectoryManager
bool IsEmpty(string path);
bool ExistsInsideDirectory(string parentDirectoryPath, string childPath);
void Delete(string path, bool recursive = false);
string GetRelativePath(string referenceFullPath, string targetFullPath);
}

public class DirectoryManager : IDirectoryManager
Expand All @@ -42,5 +43,7 @@ public bool ExistsInsideDirectory(string parentDirectoryPath, string childPath)
}

public void Delete(string path, bool recursive = false) => Directory.Delete(path, recursive);

public string GetRelativePath(string referenceFullPath, string targetFullPath) => Path.GetRelativePath(referenceFullPath, targetFullPath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,14 @@
<PackageReference Include="Moq" Version="4.16.1" />
</ItemGroup>

<ItemGroup>
<None Include="..\..\testapps\**\*">
<Link>testapps\%(RecursiveDir)/%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Remove="..\..\testapps\**\bin\**" />
<None Remove="..\..\testapps\**\obj\**" />
<None Remove="..\..\testapps\**\docker\**\*" />
</ItemGroup>

</Project>
Loading

0 comments on commit 1a9cc33

Please sign in to comment.