Skip to content

Commit

Permalink
fix: fix an issue causing container deployments to fail when run on a…
Browse files Browse the repository at this point in the history
…n ARM-based system
  • Loading branch information
philasmar committed Sep 19, 2024
1 parent da8b3eb commit 86e102f
Show file tree
Hide file tree
Showing 18 changed files with 138 additions and 51 deletions.
11 changes: 11 additions & 0 deletions .autover/changes/f3bdd1b8-155d-416d-b4ff-0d1bcf119e45.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"Projects": [
{
"Name": "AWS.Deploy.CLI",
"Type": "Patch",
"ChangelogMessages": [
"Fix an issue causing container deployments to fail when run on an ARM-based system"
]
}
]
}
2 changes: 1 addition & 1 deletion src/AWS.Deploy.CLI/AWS.Deploy.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.5.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20574.7" />
<PackageReference Include="System.Text.Json" Version="6.0.8" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync()
return Task.FromResult(AuthenticateResult.Fail("Missing Authorization header"));
}

return Task.FromResult(ProcessAuthorizationHeader(value, _encryptionProvider));
return Task.FromResult(ProcessAuthorizationHeader(value.ToString(), _encryptionProvider));
}

public static AuthenticateResult ProcessAuthorizationHeader(string authorizationHeaderValue, IEncryptionProvider encryptionProvider)
Expand Down
2 changes: 2 additions & 0 deletions src/AWS.Deploy.DockerEngine/AWS.Deploy.DockerEngine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
<ItemGroup>
<None Remove="Properties\DockerFileConfig.json" />
<None Remove="Templates\Dockerfile.template" />
<None Remove="Templates\Dockerfile.Net6.template" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Properties\DockerFileConfig.json" />
<EmbeddedResource Include="Templates\Dockerfile.Net6.template" />
<EmbeddedResource Include="Templates\Dockerfile.template" />
</ItemGroup>

Expand Down
4 changes: 2 additions & 2 deletions src/AWS.Deploy.DockerEngine/DockerEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void GenerateDockerFile(Recommendation recommendation)
DetermineHTTPPortEnvironmentVariable(recommendation, recommendation.DeploymentBundle.DockerfileHttpPort));
var projectDirectory = Path.GetDirectoryName(_projectPath) ?? "";
var projectList = GetProjectList();
dockerFile.WriteDockerFile(projectDirectory, projectList);
dockerFile.WriteDockerFile(projectDirectory, projectList, recommendation.ProjectDefinition.TargetFramework);
}

/// <summary>
Expand Down Expand Up @@ -192,7 +192,7 @@ public void DetermineDockerExecutionDirectory(Recommendation recommendation)
if (string.IsNullOrEmpty(recommendation.DeploymentBundle.DockerExecutionDirectory))
{
var projectFilename = Path.GetFileName(recommendation.ProjectPath);

if (DockerUtilities.TryGetAbsoluteDockerfile(recommendation, _fileManager, _directoryManager, out var dockerFilePath))
{
using (var stream = File.OpenRead(dockerFilePath))
Expand Down
8 changes: 4 additions & 4 deletions src/AWS.Deploy.DockerEngine/DockerFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ public DockerFile(ImageMapping imageMapping, string projectName, string? assembl
/// <summary>
/// Writes a docker file based on project information
/// </summary>
public void WriteDockerFile(string projectDirectory, List<string>? projectList)
public void WriteDockerFile(string projectDirectory, List<string>? projectList, string? targetFramework)
{
var dockerFileTemplate = ProjectUtilities.ReadTemplate();
var dockerFileTemplate = ProjectUtilities.ReadTemplate(targetFramework);
var projects = "";
var projectPath = "";
var projectFolder = "";
Expand Down Expand Up @@ -99,7 +99,7 @@ public void WriteDockerFile(string projectDirectory, List<string>? projectList)
dockerFile = dockerFile
.Replace("{http-port-env-variable}", string.Empty);
}
// For all other ports, it is up to the user to expose the HTTPS port in the dockerfile.
// For all other ports, it is up to the user to expose the HTTPS port in the dockerfile.
else
{
dockerFile = dockerFile
Expand All @@ -119,7 +119,7 @@ public void WriteDockerFile(string projectDirectory, List<string>? projectList)
.Replace("{non-root-user}", "\r\nUSER app");
}

// ProjectDefinitionParser will have transformed projectDirectory to an absolute path,
// ProjectDefinitionParser will have transformed projectDirectory to an absolute path,
// and DockerFileName is static so traversal should not be possible here.
// nosemgrep: csharp.lang.security.filesystem.unsafe-path-combine.unsafe-path-combine
File.WriteAllText(Path.Combine(projectDirectory, Constants.Docker.DefaultDockerfileName), dockerFile);
Expand Down
19 changes: 16 additions & 3 deletions src/AWS.Deploy.DockerEngine/ProjectUtilities.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using System.IO;
using System.Reflection;
using AWS.Deploy.Common;
using AWS.Deploy.Common.Extensions;
Expand All @@ -12,6 +11,7 @@ public class ProjectUtilities
{
private const string DockerFileConfig = "AWS.Deploy.DockerEngine.Properties.DockerFileConfig.json";
private const string DockerfileTemplate = "AWS.Deploy.DockerEngine.Templates.Dockerfile.template";
private const string DockerfileTemplate_Net6 = "AWS.Deploy.DockerEngine.Templates.Dockerfile.Net6.template";

/// <summary>
/// Retrieves the Docker File Config
Expand All @@ -31,9 +31,22 @@ internal static string ReadDockerFileConfig()
/// <summary>
/// Reads dockerfile template file
/// </summary>
internal static string ReadTemplate()
internal static string ReadTemplate(string? targetFramework)
{
var template = Assembly.GetExecutingAssembly().ReadEmbeddedFile(DockerfileTemplate);
string templateLocation;
switch (targetFramework)
{
case "net6.0":
case "net5.0":
case "netcoreapp3.1":
templateLocation = DockerfileTemplate_Net6;
break;

default:
templateLocation = DockerfileTemplate;
break;
}
var template = Assembly.GetExecutingAssembly().ReadEmbeddedFile(templateLocation);

if (string.IsNullOrWhiteSpace(template))
{
Expand Down
26 changes: 26 additions & 0 deletions src/AWS.Deploy.DockerEngine/Templates/Dockerfile.Net6.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
FROM {docker-base-image} AS base{non-root-user}
WORKDIR /app
{exposed-ports}

FROM {docker-build-image} AS build
WORKDIR /src
{project-path-list}
RUN dotnet restore "{project-path}"
COPY . .
WORKDIR "/src/{project-folder}"
RUN dotnet build "{project-name}" -c Release -o /app/build

FROM build AS publish
RUN apt-get update -yq \
&& apt-get install -yq ca-certificates curl gnupg \
&& mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update -yq \
&& apt-get install nodejs -yq
RUN dotnet publish "{project-name}" -c Release -o /app/publish

FROM base AS final{http-port-env-variable}
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "{assembly-name}.dll"]
9 changes: 5 additions & 4 deletions src/AWS.Deploy.DockerEngine/Templates/Dockerfile.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ FROM {docker-base-image} AS base{non-root-user}
WORKDIR /app
{exposed-ports}

FROM {docker-build-image} AS build
FROM --platform=$BUILDPLATFORM {docker-build-image} AS build
ARG TARGETARCH
WORKDIR /src
{project-path-list}
RUN dotnet restore "{project-path}"
RUN dotnet restore "{project-path}" -a $TARGETARCH
COPY . .
WORKDIR "/src/{project-folder}"
RUN dotnet build "{project-name}" -c Release -o /app/build
RUN dotnet build "{project-name}" -c Release -o /app/build -a $TARGETARCH

FROM build AS publish
RUN apt-get update -yq \
Expand All @@ -18,7 +19,7 @@ RUN apt-get update -yq \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update -yq \
&& apt-get install nodejs -yq
RUN dotnet publish "{project-name}" -c Release -o /app/publish
RUN dotnet publish "{project-name}" -c Release -o /app/publish -a $TARGETARCH

FROM base AS final{http-port-env-variable}
WORKDIR /app
Expand Down
7 changes: 2 additions & 5 deletions src/AWS.Deploy.Orchestration/AWS.Deploy.Orchestration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
<RootNamespace>AWS.Deploy.Orchestration</RootNamespace>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\AWS.Deploy.Recipes.CDK.Common\RecipeProps.cs" Link="RecipeProps.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="AWSSDK.CloudControlApi" Version="3.7.300.77" />
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.7.302.18" />
Expand All @@ -31,14 +27,15 @@
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<PackageReference Include="Microsoft.TemplateEngine.IDE" Version="5.0.1" />
<PackageReference Include="Microsoft.TemplateEngine.Orchestrator.RunnableProjects" Version="5.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="YamlDotNet" Version="13.4.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\AWS.Deploy.Recipes\AWS.Deploy.Recipes.csproj" />
<ProjectReference Include="..\AWS.Deploy.DockerEngine\AWS.Deploy.DockerEngine.csproj" />
<ProjectReference Include="..\AWS.Deploy.Recipes.CDK.Common\AWS.Deploy.Recipes.CDK.Common.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 5 additions & 0 deletions src/AWS.Deploy.Orchestration/DeploymentBundleHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public async Task BuildDockerImage(CloudApplication cloudApplication, Recommenda
DockerUtilities.TryGetAbsoluteDockerfile(recommendation, _fileManager, _directoryManager, out var dockerFile);

var dockerBuildCommand = $"docker build -t {imageTag} -f \"{dockerFile}\"{buildArgs} .";
if (RuntimeInformation.OSArchitecture != Architecture.X64)
{
dockerBuildCommand = $"docker buildx build --platform linux/amd64 -t {imageTag} -f \"{dockerFile}\"{buildArgs} .";

Check warning on line 79 in src/AWS.Deploy.Orchestration/DeploymentBundleHandler.cs

View check run for this annotation

Codecov / codecov/patch

src/AWS.Deploy.Orchestration/DeploymentBundleHandler.cs#L79

Added line #L79 was not covered by tests
}

_interactiveService.LogInfoMessage($"Docker Execution Directory: {Path.GetFullPath(dockerExecutionDirectory)}");
_interactiveService.LogInfoMessage($"Docker Build Command: {dockerBuildCommand}");

Expand Down
4 changes: 2 additions & 2 deletions src/AWS.Deploy.Orchestration/TemplateEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void GenerateCDKProjectFromTemplate(Recommendation recommendation, Orches
{
throw new InvalidOperationException($"{nameof(recommendation.Recipe.CdkProjectTemplateId)} cannot be null or an empty string");
}

//The location of the base template that will be installed into the templating engine
var cdkProjectTemplateDirectory = Path.Combine(
Path.GetDirectoryName(recommendation.Recipe.RecipePath) ??
Expand All @@ -67,7 +67,7 @@ public void GenerateCDKProjectFromTemplate(Recommendation recommendation, Orches
var templateParameters = new Dictionary<string, string> {
// CDK Template projects can parameterize the version number of the AWS.Deploy.Recipes.CDK.Common package. This avoid
// projects having to be modified every time the package version is bumped.
{ "AWSDeployRecipesCDKCommonVersion", FileVersionInfo.GetVersionInfo(typeof(Constants.CloudFormationIdentifier).Assembly.Location).ProductVersion
{ "AWSDeployRecipesCDKCommonVersion", FileVersionInfo.GetVersionInfo(typeof(AWS.Deploy.Recipes.CDK.Common.CDKRecipeSetup).Assembly.Location).ProductVersion
?? throw new InvalidAWSDeployRecipesCDKCommonVersionException(DeployToolErrorCode.InvalidAWSDeployRecipesCDKCommonVersion, "The version number of the AWS.Deploy.Recipes.CDK.Common package is invalid.") }
};

Expand Down
37 changes: 30 additions & 7 deletions test/AWS.Deploy.CLI.UnitTests/DeploymentBundleHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,16 @@ public async Task BuildDockerImage_DockerExecutionDirectoryNotSet()
var expectedDockerFile = Path.GetFullPath(Path.Combine(".", "Dockerfile"), recommendation.GetProjectDirectory());
var dockerExecutionDirectory = Directory.GetParent(Path.GetFullPath(recommendation.ProjectPath)).Parent.Parent;

Assert.Equal($"docker build -t {imageTag} -f \"{expectedDockerFile}\" .",
_commandLineWrapper.CommandsToExecute.First().Command);
if (RuntimeInformation.OSArchitecture.Equals(Architecture.X64))
{
Assert.Equal($"docker build -t {imageTag} -f \"{expectedDockerFile}\" .",
_commandLineWrapper.CommandsToExecute.First().Command);
}
else
{
Assert.Equal($"docker buildx build --platform linux/amd64 -t {imageTag} -f \"{expectedDockerFile}\" .",
_commandLineWrapper.CommandsToExecute.First().Command);
}
Assert.Equal(dockerExecutionDirectory.FullName,
_commandLineWrapper.CommandsToExecute.First().WorkingDirectory);
}
Expand All @@ -116,8 +124,16 @@ public async Task BuildDockerImage_DockerExecutionDirectorySet()

var expectedDockerFile = Path.GetFullPath(Path.Combine(".", "Dockerfile"), recommendation.GetProjectDirectory());

Assert.Equal($"docker build -t {imageTag} -f \"{expectedDockerFile}\" .",
_commandLineWrapper.CommandsToExecute.First().Command);
if (RuntimeInformation.OSArchitecture.Equals(Architecture.X64))
{
Assert.Equal($"docker build -t {imageTag} -f \"{expectedDockerFile}\" .",
_commandLineWrapper.CommandsToExecute.First().Command);
}
else
{
Assert.Equal($"docker buildx build --platform linux/amd64 -t {imageTag} -f \"{expectedDockerFile}\" .",
_commandLineWrapper.CommandsToExecute.First().Command);
}
Assert.Equal(projectPath,
_commandLineWrapper.CommandsToExecute.First().WorkingDirectory);
}
Expand All @@ -144,9 +160,16 @@ public async Task BuildDockerImage_AlternativeDockerfilePathSet()
var cloudApplication = new CloudApplication("ConsoleAppTask", string.Empty, CloudApplicationResourceType.CloudFormationStack, recommendation.Recipe.Id);
var imageTag = "imageTag";
await _deploymentBundleHandler.BuildDockerImage(cloudApplication, recommendation, imageTag);

Assert.Equal($"docker build -t {imageTag} -f \"{dockerfilePath}\" .",
_commandLineWrapper.CommandsToExecute.First().Command);
if (RuntimeInformation.OSArchitecture.Equals(Architecture.X64))
{
Assert.Equal($"docker build -t {imageTag} -f \"{dockerfilePath}\" .",
_commandLineWrapper.CommandsToExecute.First().Command);
}
else
{
Assert.Equal($"docker buildx build --platform linux/amd64 -t {imageTag} -f \"{dockerfilePath}\" .",
_commandLineWrapper.CommandsToExecute.First().Command);
}
Assert.Equal(expectedDockerExecutionDirectory.FullName,
_commandLineWrapper.CommandsToExecute.First().WorkingDirectory);
}
Expand Down
11 changes: 8 additions & 3 deletions test/AWS.Deploy.CLI.UnitTests/DockerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,15 @@ public void DockerFileConfigExists()
Assert.False(string.IsNullOrWhiteSpace(dockerFileConfig));
}

[Fact]
public void DockerfileTemplateExists()
[Theory]
[InlineData("net8.0")]
[InlineData("net7.0")]
[InlineData("net6.0")]
[InlineData("net5.0")]
[InlineData("netcoreapp3.1")]
public void DockerfileTemplateExists(string targetFramework)
{
var dockerFileTemplate = ProjectUtilities.ReadTemplate();
var dockerFileTemplate = ProjectUtilities.ReadTemplate(targetFramework);
Assert.False(string.IsNullOrWhiteSpace(dockerFileTemplate));
}

Expand Down
11 changes: 6 additions & 5 deletions testapps/docker/WebAppNet7/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:7.0 AS build
ARG TARGETARCH
WORKDIR /src
COPY ["WebAppNet7.csproj", ""]
RUN dotnet restore "WebAppNet7.csproj"
RUN dotnet restore "WebAppNet7.csproj" -a $TARGETARCH
COPY . .
WORKDIR "/src/"
RUN dotnet build "WebAppNet7.csproj" -c Release -o /app/build
RUN dotnet build "WebAppNet7.csproj" -c Release -o /app/build -a $TARGETARCH

FROM build AS publish
RUN apt-get update -yq \
Expand All @@ -19,7 +20,7 @@ RUN apt-get update -yq \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update -yq \
&& apt-get install nodejs -yq
RUN dotnet publish "WebAppNet7.csproj" -c Release -o /app/publish
RUN dotnet publish "WebAppNet7.csproj" -c Release -o /app/publish -a $TARGETARCH

FROM base AS final
WORKDIR /app
Expand Down
9 changes: 5 additions & 4 deletions testapps/docker/WebAppNet7/ReferenceDockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:7.0 AS build
ARG TARGETARCH
WORKDIR /src
COPY ["WebAppNet7.csproj", ""]
RUN dotnet restore "WebAppNet7.csproj"
RUN dotnet restore "WebAppNet7.csproj" -a $TARGETARCH
COPY . .
WORKDIR "/src/"
RUN dotnet build "WebAppNet7.csproj" -c Release -o /app/build
RUN dotnet build "WebAppNet7.csproj" -c Release -o /app/build -a $TARGETARCH

FROM build AS publish
RUN apt-get update -yq \
Expand All @@ -19,7 +20,7 @@ RUN apt-get update -yq \
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
&& apt-get update -yq \
&& apt-get install nodejs -yq
RUN dotnet publish "WebAppNet7.csproj" -c Release -o /app/publish
RUN dotnet publish "WebAppNet7.csproj" -c Release -o /app/publish -a $TARGETARCH

FROM base AS final
WORKDIR /app
Expand Down
Loading

0 comments on commit 86e102f

Please sign in to comment.