Skip to content

Commit

Permalink
updating file properties for self-contained zip deployments (#529)
Browse files Browse the repository at this point in the history
* updating file properties for self-contained zip deployments

* retrying delete

* fixes

* simplifying

* another simplification
  • Loading branch information
brettsam authored Jul 21, 2021
1 parent c9c5256 commit 3649307
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 26 deletions.
3 changes: 2 additions & 1 deletion sdk/Sdk/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.Azure.Functions.SdkTests, PublicKey=00240000048000009400000006020000002400005253413100040000010001005148be37ac1d9f58bd40a2e472c9d380d635b6048278f7d47480b08c928858f0f7fe17a6e4ce98da0e7a7f0b8c308aecd9e9b02d7e9680a5b5b75ac7773cec096fbbc64aebd429e77cb5f89a569a79b28e9c76426783f624b6b70327eb37341eb498a2c3918af97c4860db6cdca4732787150841e395a29cfacb959c1fd971c1")]
[assembly: InternalsVisibleTo("Microsoft.Azure.Functions.SdkE2ETests, PublicKey=00240000048000009400000006020000002400005253413100040000010001005148be37ac1d9f58bd40a2e472c9d380d635b6048278f7d47480b08c928858f0f7fe17a6e4ce98da0e7a7f0b8c308aecd9e9b02d7e9680a5b5b75ac7773cec096fbbc64aebd429e77cb5f89a569a79b28e9c76426783f624b6b70327eb37341eb498a2c3918af97c4860db6cdca4732787150841e395a29cfacb959c1fd971c1")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=00240000048000009400000006020000002400005253413100040000010001005148be37ac1d9f58bd40a2e472c9d380d635b6048278f7d47480b08c928858f0f7fe17a6e4ce98da0e7a7f0b8c308aecd9e9b02d7e9680a5b5b75ac7773cec096fbbc64aebd429e77cb5f89a569a79b28e9c76426783f624b6b70327eb37341eb498a2c3918af97c4860db6cdca4732787150841e395a29cfacb959c1fd971c1")]
3 changes: 2 additions & 1 deletion sdk/Sdk/Sdk.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<PatchProductVersion>3</PatchProductVersion>
<PatchProductVersion>4</PatchProductVersion>
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
<PackageId>Microsoft.Azure.Functions.Worker.Sdk</PackageId>
<Description>This package provides development time support for the Azure Functions .NET Worker.</Description>
Expand Down Expand Up @@ -31,6 +31,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.9.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
<PackageReference Include="SharpZipLib" Version ="1.3.2" />
<PackageReference Include="System.Text.Json" Version="5.0.2" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />

Expand Down
128 changes: 124 additions & 4 deletions sdk/Sdk/Tasks/ZipDeploy/CreateZipFileTask.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System;
using System.IO;
using System.IO.Compression;
using System.Text.Json;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

Expand All @@ -20,6 +22,11 @@ public class CreateZipFileTask : AppDomainIsolatedTask
public class CreateZipFileTask : Task
#endif
{
internal const string WorkerRootReplacement = "{WorkerRoot}";

// Unix file permissions for -rwxrwxrwx
internal static readonly int UnixExecutablePermissions = Convert.ToInt32("100777", 8) << 16;

[Required]
public string? FolderToZip { get; set; }

Expand All @@ -34,10 +41,123 @@ public class CreateZipFileTask : Task

public override bool Execute()
{
if (FolderToZip == null)
{
return false;
}

string zipFileName = ProjectName + " - " + DateTime.Now.ToString("yyyyMMddHHmmssFFF") + ".zip";
CreatedZipPath = Path.Combine(PublishIntermediateTempPath, zipFileName);
ZipFile.CreateFromDirectory(FolderToZip, CreatedZipPath);

CreateZipFileFromDirectory(FolderToZip, CreatedZipPath);

return true;
}

internal static void CreateZipFileFromDirectory(string directoryToZip, string destinationArchiveFileName)
{
System.IO.Compression.ZipFile.CreateFromDirectory(directoryToZip, destinationArchiveFileName);

// We may need to modify permissions for Linux.
string configJson = File.ReadAllText(Path.Combine(directoryToZip, "worker.config.json"));
var configJsonDoc = JsonDocument.Parse(configJson);

if (configJsonDoc.RootElement.TryGetProperty("description", out JsonElement description) &&
description.TryGetProperty("defaultExecutablePath", out JsonElement defaultExecutablePath) &&
defaultExecutablePath.ValueKind == JsonValueKind.String)
{
string? executable = defaultExecutablePath.GetString();

// if the executable path contains "{WorkerRoot}", it means we'll be executing this file directly (as
// opposed to running with 'dotnet ...'). If so, we need to make the file executable for Linux.
if (executable != null && executable.Contains(WorkerRootReplacement))
{
executable = executable.Replace(WorkerRootReplacement, string.Empty);

if (!string.IsNullOrEmpty(executable))
{
ModifyUnixFilePermissions(destinationArchiveFileName, directoryToZip, executable);
}
}
}
}

internal static void ModifyUnixFilePermissions(string zipFilePath, string entryRootPath, string entryName)
{
using (var zipFile = new ZipFile(zipFilePath))
{
zipFile.EntryFactory = new EntryFactory();
zipFile.BeginUpdate();

try
{
string entryFullPath = Path.Combine(entryRootPath, entryName);

// In Windows, we leave off the .exe, so need to adjust the entry name
if (!File.Exists(entryFullPath))
{
entryFullPath += ".exe";
entryName += ".exe";

if (!File.Exists(entryFullPath))
{
return;
}
}

// This will overwrite the existing entry.
zipFile.Add(entryFullPath, entryName);
}
finally
{
zipFile.CommitUpdate();
zipFile.Close();
}
}
}

private class EntryFactory : IEntryFactory
{
private IEntryFactory _internal = new ZipEntryFactory();

public INameTransform NameTransform { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

public ZipEntryFactory.TimeSetting Setting => throw new NotImplementedException();

public DateTime FixedDateTime => throw new NotImplementedException();

public ZipEntry MakeDirectoryEntry(string directoryName)
{
throw new NotImplementedException();
}

public ZipEntry MakeDirectoryEntry(string directoryName, bool useFileSystem)
{
throw new NotImplementedException();
}

public ZipEntry MakeFileEntry(string fileName)
{
throw new NotImplementedException();
}

public ZipEntry MakeFileEntry(string fileName, bool useFileSystem)
{
throw new NotImplementedException();
}

public ZipEntry MakeFileEntry(string fileName, string entryName, bool useFileSystem)
{
var zipEntry = _internal.MakeFileEntry(fileName, entryName, useFileSystem);

// Unix
zipEntry.HostSystem = 3;

// Unix file permissions for -rwxrwxrwx
zipEntry.ExternalFileAttributes = UnixExecutablePermissions;

return zipEntry;
}
}
}
}
1 change: 1 addition & 0 deletions test/FunctionMetadataGeneratorTests/SdkTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
<PackageReference Include="Moq" Version="4.16.1" />
Expand Down
19 changes: 3 additions & 16 deletions test/SdkE2ETests/PublishTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,10 @@ public async Task Publish_Rid()

private async Task RunPublishTest(string outputDir, string additionalParams = null)
{
// Name of the csproj
string projectNameToTest = "FunctionApp";
string projectFileDirectory = Path.Combine(TestUtility.SamplesRoot, projectNameToTest);
// Name of the csproj
string projectFileDirectory = Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj");

// Restore
_testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] Restoring...");
string dotnetArgs = $"restore {projectNameToTest}.csproj --source {TestUtility.LocalPackages}";
int? exitCode = await new ProcessWrapper().RunProcess(TestUtility.DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: _testOutputHelper);
Assert.True(exitCode.HasValue && exitCode.Value == 0);
_testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done.");

// Publish
_testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] Publishing...");
dotnetArgs = $"publish {projectNameToTest}.csproj --configuration {TestUtility.Configuration} -o {outputDir} {additionalParams}";
exitCode = await new ProcessWrapper().RunProcess(TestUtility.DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: _testOutputHelper);
Assert.True(exitCode.HasValue && exitCode.Value == 0);
_testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done.");
await TestUtility.RestoreAndPublishProjectAsync(projectFileDirectory, outputDir, additionalParams, _testOutputHelper);

// Make sure files are in /.azurefunctions
string azureFunctionsDir = Path.Combine(outputDir, ".azurefunctions");
Expand Down
12 changes: 10 additions & 2 deletions test/SdkE2ETests/SdkE2ETests.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net5.0</TargetFramework>
<AssemblyName>Microsoft.Azure.Functions.SdkE2ETests</AssemblyName>
<RootNamespace>Microsoft.Azure.Functions.SdkE2ETests</RootNamespace>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\key.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup>
Expand All @@ -15,6 +17,8 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.9.0" />
<PackageReference Include="SharpZipLib" Version ="1.3.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
Expand All @@ -23,4 +27,8 @@
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\sdk\Sdk\Sdk.csproj" />
</ItemGroup>

</Project>
26 changes: 24 additions & 2 deletions test/SdkE2ETests/TestUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,38 @@ public static void ValidateFunctionsMetadata(string actualFilePath, string embed
}
}

public static async Task RestoreAndPublishProjectAsync(string fullPathToProjFile, string outputDir, string additionalParams, ITestOutputHelper outputHelper)
{
// Name of the csproj
string projectNameToTest = Path.GetFileName(fullPathToProjFile);
string projectFileDirectory = Path.GetDirectoryName(fullPathToProjFile);

// Restore
outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Restoring...");
string dotnetArgs = $"restore {projectNameToTest} --source {TestUtility.LocalPackages}";
int? exitCode = await new ProcessWrapper().RunProcess(TestUtility.DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper);
Assert.True(exitCode.HasValue && exitCode.Value == 0);
outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done.");

// Publish
outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Publishing...");
dotnetArgs = $"publish {projectNameToTest} --configuration {TestUtility.Configuration} -o {outputDir} {additionalParams}";
exitCode = await new ProcessWrapper().RunProcess(TestUtility.DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper);
Assert.True(exitCode.HasValue && exitCode.Value == 0);
outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done.");
}

private static string InitializeOutputDir(string testName)
{
string outputDir = Path.Combine(TestOutputDir, testName);

if (Directory.Exists(outputDir))
{
Directory.Delete(outputDir, true);
Directory.CreateDirectory(outputDir);
Directory.Delete(outputDir, recursive: true);
}

Directory.CreateDirectory(outputDir);

return outputDir;
}

Expand Down
66 changes: 66 additions & 0 deletions test/SdkE2ETests/ZipDeployTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System.IO;
using System.Threading.Tasks;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Azure.Functions.SdkE2ETests;
using Microsoft.NET.Sdk.Functions.MSBuild.Tasks;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.Azure.Functions.SdkTests
{
public class ZipDeployTests
{
private ITestOutputHelper _testOutputHelper;

public ZipDeployTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}

[Theory]
[InlineData("linux-x64", true)]
[InlineData("win-x64", false)]
[InlineData("win-x64", true)]
public async Task CreateZipFileFromDirectory_SetsExecutableFlag_WhenSelfContained(string rid, bool selfContained)
{
string testName = nameof(CreateZipFileFromDirectory_SetsExecutableFlag_WhenSelfContained);
string directoryToZip = await TestUtility.InitializeTestAsync(_testOutputHelper, testName);

string zipName = Path.Combine(Directory.GetParent(directoryToZip).FullName, $"{testName}.zip");

if (File.Exists(zipName))
{
File.Delete(zipName);
}

string projectFileDirectory = Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj");

await TestUtility.RestoreAndPublishProjectAsync(projectFileDirectory, directoryToZip, $"-r {rid} --self-contained {selfContained}", _testOutputHelper);

CreateZipFileTask.CreateZipFileFromDirectory(directoryToZip, zipName);

using (var zip = new ZipFile(zipName))
{
Assert.Equal(Directory.GetFiles(directoryToZip, "*", SearchOption.AllDirectories).Length, zip.Count);

for (int i = 0; i < zip.Count; i++)
{
var entry = zip[i];
if (selfContained &&
(entry.Name == "FunctionApp" || entry.Name == "FunctionApp.exe"))
{
Assert.Equal(3, entry.HostSystem);
Assert.Equal(CreateZipFileTask.UnixExecutablePermissions, entry.ExternalFileAttributes);
}
else
{
Assert.Equal(0, entry.HostSystem);
Assert.Equal(0, entry.ExternalFileAttributes);
}
}

zip.Close();
}
}
}
}

0 comments on commit 3649307

Please sign in to comment.