Skip to content

Commit

Permalink
Merge pull request #337 from aws/dev
Browse files Browse the repository at this point in the history
chore: release 0.20
  • Loading branch information
96malhar authored Sep 28, 2021
2 parents 4148988 + d9fa514 commit 48a9adc
Show file tree
Hide file tree
Showing 33 changed files with 442 additions and 316 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/codebuild-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: AWS CodeBuild CI

on:
push:
branches:
- main
- dev
pull_request:
branches:
- main
- dev

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.CI_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.CI_AWS_ACCESS_KEY_SECRET }}
aws-region: us-west-2
- name: Run CodeBuild
uses: aws-actions/[email protected]
with:
project-name: aws-dotnet-deploy-ci
61 changes: 33 additions & 28 deletions src/AWS.Deploy.CLI/AWSUtilities.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Amazon.Runtime;
using Amazon.Runtime.CredentialManagement;
using Amazon.EC2.Model;
using System.IO;
using AWS.Deploy.CLI.Commands.CommandHandlerInput;
using AWS.Deploy.CLI.Utilities;
using AWS.Deploy.Common;
using AWS.Deploy.Common.IO;

namespace AWS.Deploy.CLI
Expand All @@ -25,7 +28,10 @@ public class AWSUtilities : IAWSUtilities
private readonly IConsoleUtilities _consoleUtilities;
private readonly IDirectoryManager _directoryManager;

public AWSUtilities(IToolInteractiveService toolInteractiveService, IConsoleUtilities consoleUtilities, IDirectoryManager directoryManager)
public AWSUtilities(
IToolInteractiveService toolInteractiveService,
IConsoleUtilities consoleUtilities,
IDirectoryManager directoryManager)
{
_toolInteractiveService = toolInteractiveService;
_consoleUtilities = consoleUtilities;
Expand All @@ -36,51 +42,52 @@ public async Task<AWSCredentials> ResolveAWSCredentials(string? profileName, str
{
async Task<AWSCredentials> Resolve()
{
var chain = new CredentialProfileStoreChain();
var chain = new CredentialProfileStoreChain();

if (!string.IsNullOrEmpty(profileName) && chain.TryGetAWSCredentials(profileName, out var profileCredentials) &&
// Skip checking CanLoadCredentials for AssumeRoleAWSCredentials because it might require an MFA token and the callback hasn't been setup yet.
(profileCredentials is AssumeRoleAWSCredentials || await CanLoadCredentials(profileCredentials)))
{
{
_toolInteractiveService.WriteLine($"Configuring AWS Credentials from Profile {profileName}.");
return profileCredentials;
}

if (!string.IsNullOrEmpty(lastUsedProfileName) &&
if (!string.IsNullOrEmpty(lastUsedProfileName) &&
chain.TryGetAWSCredentials(lastUsedProfileName, out var lastUsedCredentials) &&
await CanLoadCredentials(lastUsedCredentials))
{
_toolInteractiveService.WriteLine($"Configuring AWS Credentials with previous configured profile value {lastUsedProfileName}.");
{
_toolInteractiveService.WriteLine($"Configuring AWS Credentials with previous configured profile value {lastUsedProfileName}.");
return lastUsedCredentials;
}
}

try
{
try
{
var fallbackCredentials = FallbackCredentialsFactory.GetCredentials();

if (await CanLoadCredentials(fallbackCredentials))
{
_toolInteractiveService.WriteLine("Configuring AWS Credentials using AWS SDK credential search.");
{
_toolInteractiveService.WriteLine("Configuring AWS Credentials using AWS SDK credential search.");
return fallbackCredentials;
}
}
catch (AmazonServiceException ex)
{
// FallbackCredentialsFactory throws an exception if no credentials are found. Burying exception because if no credentials are found
// we want to continue and ask the user to select a profile.
_toolInteractiveService.WriteDebugLine(ex.PrettyPrint());
}
}
catch (AmazonServiceException)
{
// FallbackCredentialsFactory throws an exception if no credentials are found. Burying exception because if no credentials are found
// we want to continue and ask the user to select a profile.
}

var sharedCredentials = new SharedCredentialsFile();
if (sharedCredentials.ListProfileNames().Count == 0)
{
throw new NoAWSCredentialsFoundException("Unable to resolve AWS credentials to access AWS.");
}
var sharedCredentials = new SharedCredentialsFile();
if (sharedCredentials.ListProfileNames().Count == 0)
{
throw new NoAWSCredentialsFoundException("Unable to resolve AWS credentials to access AWS.");
}

var selectedProfileName = _consoleUtilities.AskUserToChoose(sharedCredentials.ListProfileNames(), "Select AWS Credentials Profile", null);
var selectedProfileName = _consoleUtilities.AskUserToChoose(sharedCredentials.ListProfileNames(), "Select AWS Credentials Profile", null);

if (chain.TryGetAWSCredentials(selectedProfileName, out var selectedProfileCredentials) &&
(await CanLoadCredentials(selectedProfileCredentials)))
{
{
return selectedProfileCredentials;
}

Expand All @@ -100,16 +107,14 @@ await CanLoadCredentials(lastUsedCredentials))

private async Task<bool> CanLoadCredentials(AWSCredentials credentials)
{
if (null == credentials)
return false;

try
{
await credentials.GetCredentialsAsync();
return true;
}
catch
catch (Exception ex)
{
_toolInteractiveService.WriteDebugLine(ex.PrettyPrint());
return false;
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/AWS.Deploy.CLI/CloudFormation/StackEventMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ internal class StackEventMonitor
private readonly IAmazonCloudFormation _cloudFormationClient;
private readonly HashSet<string> _processedEventIds = new HashSet<string>();
private readonly IConsoleUtilities _consoleUtilities;
private readonly IToolInteractiveService _interactiveService;

public StackEventMonitor(string stackName, IAWSClientFactory awsClientFactory, IConsoleUtilities consoleUtilities)
public StackEventMonitor(string stackName, IAWSClientFactory awsClientFactory, IConsoleUtilities consoleUtilities, IToolInteractiveService interactiveService)
{
_stackName = stackName;
_consoleUtilities = consoleUtilities;
_interactiveService = interactiveService;

_cloudFormationClient = awsClientFactory.GetAWSClient<IAmazonCloudFormation>();
}
Expand Down Expand Up @@ -112,10 +114,12 @@ private async Task ReadNewEventsAsync()
catch (AmazonCloudFormationException exception) when (exception.ErrorCode.Equals("ValidationError") && exception.Message.Equals($"Stack [{_stackName}] does not exist"))
{
// Stack is deleted, there could be some missed events between the last poll timestamp and DELETE_COMPLETE
_interactiveService.WriteDebugLine(exception.PrettyPrint());
}
catch (AmazonCloudFormationException)
catch (AmazonCloudFormationException exception)
{
// Other AmazonCloudFormationException
_interactiveService.WriteDebugLine(exception.PrettyPrint());
}

foreach (var stackEvent in stackEvents.OrderBy(e => e.Timestamp))
Expand Down
3 changes: 2 additions & 1 deletion src/AWS.Deploy.CLI/Commands/DeleteDeploymentCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public async Task ExecuteAsync(string stackName)
}

_interactiveService.WriteLine($"{stackName}: deleting...");
var monitor = new StackEventMonitor(stackName, _awsClientFactory, _consoleUtilities);
var monitor = new StackEventMonitor(stackName, _awsClientFactory, _consoleUtilities, _interactiveService);

try
{
Expand Down Expand Up @@ -171,6 +171,7 @@ private async Task WaitForStackDelete(string stackName)
}
catch (AmazonCloudFormationException exception) when (exception.ErrorCode.Equals("ValidationError") && exception.Message.Equals($"Stack with id {stackName} does not exist"))
{
_interactiveService.WriteDebugLine(exception.PrettyPrint());
shouldRetry = false;
}
catch (AmazonCloudFormationException exception) when (exception.ErrorCode.Equals("Throttling"))
Expand Down
10 changes: 7 additions & 3 deletions src/AWS.Deploy.CLI/Commands/DeployCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ private void DisplayOutputResources(List<DisplayedResourceItem> displayedResourc

// 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));

Expand Down Expand Up @@ -354,8 +354,9 @@ private void ConfigureDeploymentFromConfigFile(Recommendation recommendation, Us
throw new InvalidOverrideValueException($"Invalid value {optionSettingValue} for option setting item {optionSettingJsonPath}");
}
}
catch (Exception)
catch (Exception exception)
{
_toolInteractiveService.WriteDebugLine(exception.PrettyPrint());
throw new InvalidOverrideValueException($"Invalid value {optionSettingValue} for option setting item {optionSettingJsonPath}");
}

Expand Down Expand Up @@ -483,7 +484,10 @@ private string AskUserForCloudApplicationName(ProjectDefinition project, List<Cl
{
defaultName = _cloudApplicationNameGenerator.GenerateValidName(project, existingApplications);
}
catch { }
catch (Exception exception)
{
_toolInteractiveService.WriteDebugLine(exception.PrettyPrint());
}

var cloudApplicationName = "";

Expand Down
4 changes: 3 additions & 1 deletion src/AWS.Deploy.CLI/Commands/ServerModeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Threading.Tasks;
using AWS.Deploy.CLI.ServerMode;
using AWS.Deploy.CLI.ServerMode.Services;
using AWS.Deploy.Common;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

Expand Down Expand Up @@ -65,8 +66,9 @@ public ServerModeCommand(IToolInteractiveService interactiveService, int port, i
process.EnableRaisingEvents = true;
process.Exited += async (sender, args) => { await ShutDownHost(host, cancellationToken); };
}
catch (Exception)
catch (Exception exception)
{
_interactiveService.WriteDebugLine(exception.PrettyPrint());
return;
}

Expand Down
5 changes: 5 additions & 0 deletions src/AWS.Deploy.CLI/Utilities/CommandLineWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,12 @@ public async Task Run(
while (true)
{
if (process.HasExited)
{
// In some cases, process might have exited but OutputDataReceived or ErrorDataReceived could still be writing
// asynchronously, adding a delay should cover most of the cases.
await Task.Delay(TimeSpan.FromSeconds(1), cancelToken);
break;
}

await Task.Delay(TimeSpan.FromMilliseconds(50), cancelToken);
}
Expand Down
8 changes: 5 additions & 3 deletions src/AWS.Deploy.Orchestration/CdkProjectHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public interface ICdkProjectHandler
Task<string> ConfigureCdkProject(OrchestratorSession session, CloudApplication cloudApplication, Recommendation recommendation);
string CreateCdkProject(Recommendation recommendation, OrchestratorSession session, string? saveDirectoryPath = null);
Task DeployCdkProject(OrchestratorSession session, string cdkProjectPath, Recommendation recommendation);
void DeleteTemporaryCdkProject(string cdkProjectPath);
void DeleteTemporaryCdkProject(OrchestratorSession session, string cdkProjectPath);
}

public class CdkProjectHandler : ICdkProjectHandler
Expand Down Expand Up @@ -83,6 +83,7 @@ await _commandLineWrapper.Run($"npx cdk bootstrap aws://{session.AWSAccountId}/{
workingDirectory: cdkProjectPath,
environmentVariables: environmentVariables,
needAwsCredentials: true,
redirectIO: true,
streamOutputToInteractiveService: true);

if (cdkDeploy.ExitCode != 0)
Expand Down Expand Up @@ -118,7 +119,7 @@ public string CreateCdkProject(Recommendation recommendation, OrchestratorSessio
return saveCdkDirectoryPath;
}

public void DeleteTemporaryCdkProject(string cdkProjectPath)
public void DeleteTemporaryCdkProject(OrchestratorSession session, string cdkProjectPath)
{
var parentPath = Path.GetFullPath(Constants.CDK.ProjectsDirectory);
cdkProjectPath = Path.GetFullPath(cdkProjectPath);
Expand All @@ -130,8 +131,9 @@ public void DeleteTemporaryCdkProject(string cdkProjectPath)
{
_directoryManager.Delete(cdkProjectPath, true);
}
catch (Exception)
catch (Exception exception)
{
_interactiveService.LogDebugLine(exception.PrettyPrint());
_interactiveService.LogErrorMessageLine($"We were unable to delete the temporary project that was created for this deployment. Please manually delete it at this location: {cdkProjectPath}");
}
}
Expand Down
13 changes: 9 additions & 4 deletions src/AWS.Deploy.Orchestration/CustomRecipeLocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class CustomRecipeLocator : ICustomRecipeLocator
private readonly ICommandLineWrapper _commandLineWrapper;
private readonly IDeploymentManifestEngine _deploymentManifestEngine;
private readonly IDirectoryManager _directoryManager;

public CustomRecipeLocator(IDeploymentManifestEngine deploymentManifestEngine, IOrchestratorInteractiveService orchestratorInteractiveService,
ICommandLineWrapper commandLineWrapper, IDirectoryManager directoryManager)
{
Expand Down Expand Up @@ -109,7 +109,7 @@ private async Task<List<string>> LocateAlternateRecipePaths(string targetApplica
rootDirectoryPath = solutionDirectoryPath;
}

return GetRecipePathsFromRootDirectory(rootDirectoryPath);
return GetRecipePathsFromRootDirectory(rootDirectoryPath);
}

/// <summary>
Expand Down Expand Up @@ -141,10 +141,15 @@ private List<string> GetRecipePathsFromRootDirectory(string? rootDirectoryPath)
private async Task<string?> GetSourceControlRootDirectory(string currentDirectoryPath)
{
var possibleRootDirectoryPath = currentDirectoryPath;
while (currentDirectoryPath != null && await IsDirectoryUnderSourceControl(currentDirectoryPath))
while (await IsDirectoryUnderSourceControl(currentDirectoryPath))
{
possibleRootDirectoryPath = currentDirectoryPath;
currentDirectoryPath = _directoryManager.GetDirectoryInfo(currentDirectoryPath).Parent.FullName;
var currentDirectoryInfo = _directoryManager.GetDirectoryInfo(currentDirectoryPath);
if (currentDirectoryInfo.Parent == null)
{
break;
}
currentDirectoryPath = currentDirectoryInfo.Parent.FullName;
}
return possibleRootDirectoryPath;
}
Expand Down
10 changes: 5 additions & 5 deletions src/AWS.Deploy.Orchestration/DeploymentBundleHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public async Task<string> BuildDockerImage(CloudApplication cloudApplication, Re

recommendation.DeploymentBundle.DockerExecutionDirectory = dockerExecutionDirectory;

var result = await _commandLineWrapper.TryRunWithResult(dockerBuildCommand, dockerExecutionDirectory, redirectIO: false);
var result = await _commandLineWrapper.TryRunWithResult(dockerBuildCommand, dockerExecutionDirectory, streamOutputToInteractiveService: true);
if (result.ExitCode != 0)
{
throw new DockerBuildFailedException(result.StandardError ?? "");
Expand Down Expand Up @@ -117,7 +117,7 @@ public async Task<string> CreateDotnetPublishZip(Recommendation recommendation)
publishCommand += " --self-contained true";
}

var result = await _commandLineWrapper.TryRunWithResult(publishCommand, redirectIO: false);
var result = await _commandLineWrapper.TryRunWithResult(publishCommand, streamOutputToInteractiveService: true);
if (result.ExitCode != 0)
{
throw new DotnetPublishFailedException(result.StandardError ?? "");
Expand Down Expand Up @@ -202,7 +202,7 @@ private async Task InitiateDockerLogin()
var decodedTokens = authToken.Split(':');

var dockerLoginCommand = $"docker login --username {decodedTokens[0]} --password {decodedTokens[1]} {authorizationTokens[0].ProxyEndpoint}";
var result = await _commandLineWrapper.TryRunWithResult(dockerLoginCommand);
var result = await _commandLineWrapper.TryRunWithResult(dockerLoginCommand, streamOutputToInteractiveService: true);

if (result.ExitCode != 0)
throw new DockerLoginFailedException("Failed to login to Docker");
Expand All @@ -225,7 +225,7 @@ private async Task<Repository> SetupECRRepository(string ecrRepositoryName)
private async Task TagDockerImage(string sourceTagName, string targetTagName)
{
var dockerTagCommand = $"docker tag {sourceTagName} {targetTagName}";
var result = await _commandLineWrapper.TryRunWithResult(dockerTagCommand);
var result = await _commandLineWrapper.TryRunWithResult(dockerTagCommand, streamOutputToInteractiveService: true);

if (result.ExitCode != 0)
throw new DockerTagFailedException("Failed to tag Docker image");
Expand All @@ -234,7 +234,7 @@ private async Task TagDockerImage(string sourceTagName, string targetTagName)
private async Task PushDockerImage(string targetTagName)
{
var dockerPushCommand = $"docker push {targetTagName}";
var result = await _commandLineWrapper.TryRunWithResult(dockerPushCommand, redirectIO: false);
var result = await _commandLineWrapper.TryRunWithResult(dockerPushCommand, streamOutputToInteractiveService: true);

if (result.ExitCode != 0)
throw new DockerPushFailedException("Failed to push Docker Image");
Expand Down
Loading

0 comments on commit 48a9adc

Please sign in to comment.