Skip to content

Commit 0e4c13f

Browse files
committed
[LinuxConsumption] Support mounting Azure file share with Run-From-Package
Currently either Azure File share can be mounted or app can use Run-From-Package but not both simultaneously. This PR adds support for this scenario but only for PowerShell apps. If AzureFiles are configured, it will be mounted at /home. If Run-From-Pkg is configured the contents will be made available at /home/site/wwwroot. Note: It is possible for /home/site/wwwroot to point to a shared remote share. So copying contents to /home/site/wwwroot can be disruptive for other running instances. As a workaround /local/sitepackages will be used as a per-instance local storage where the zip will extracted before it gets mounted to /home/site/wwwwroot. Fixes #7252
1 parent 6b95141 commit 0e4c13f

26 files changed

+5347
-353
lines changed

src/WebJobs.Script.WebHost/Management/IMeshServiceClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,7 @@ public interface IMeshServiceClient
1919
Task PublishContainerActivity(IEnumerable<ContainerFunctionExecutionActivity> activities);
2020

2121
Task NotifyHealthEvent(ContainerHealthEventType healthEventType, Type source, string details);
22+
23+
Task CreateBindMount(string sourcePath, string targetPath);
2224
}
2325
}

src/WebJobs.Script.WebHost/Management/InstanceManager.cs

Lines changed: 46 additions & 233 deletions
Large diffs are not rendered by default.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Diagnostics;
6+
using System.IO;
7+
using System.IO.Compression;
8+
using Microsoft.Azure.WebJobs.Script.Diagnostics;
9+
using Microsoft.Extensions.Logging;
10+
11+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Management.LinuxSpecialization
12+
{
13+
public class BashCommandHandler : IBashCommandHandler
14+
{
15+
public const string FileCommand = "file";
16+
17+
private readonly IMetricsLogger _metricsLogger;
18+
private readonly ILogger<BashCommandHandler> _logger;
19+
20+
public BashCommandHandler(IMetricsLogger metricsLogger, ILogger<BashCommandHandler> logger)
21+
{
22+
_metricsLogger = metricsLogger ?? throw new ArgumentNullException(nameof(metricsLogger));
23+
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
24+
}
25+
26+
public (string, string, int) RunBashCommand(string command, string metricName)
27+
{
28+
try
29+
{
30+
using (_metricsLogger.LatencyEvent(metricName))
31+
{
32+
var process = new Process
33+
{
34+
StartInfo = new ProcessStartInfo
35+
{
36+
FileName = "bash",
37+
Arguments = $"-c \"{command}\"",
38+
RedirectStandardOutput = true,
39+
RedirectStandardError = true,
40+
UseShellExecute = false,
41+
CreateNoWindow = true
42+
}
43+
};
44+
_logger.LogInformation($"Running: {process.StartInfo.FileName} {process.StartInfo.Arguments}");
45+
process.Start();
46+
var output = process.StandardOutput.ReadToEnd().Trim();
47+
var error = process.StandardError.ReadToEnd().Trim();
48+
process.WaitForExit();
49+
_logger.LogInformation($"Output: {output}");
50+
if (process.ExitCode != 0)
51+
{
52+
_logger.LogError(error);
53+
}
54+
else
55+
{
56+
_logger.LogInformation($"error: {error}");
57+
}
58+
_logger.LogInformation($"exitCode: {process.ExitCode}");
59+
return (output, error, process.ExitCode);
60+
}
61+
}
62+
catch (Exception e)
63+
{
64+
_logger.LogError("Error running bash", e);
65+
}
66+
67+
return (string.Empty, string.Empty, -1);
68+
}
69+
}
70+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Management.LinuxSpecialization
5+
{
6+
public interface IBashCommandHandler
7+
{
8+
(string, string, int) RunBashCommand(string command, string metricName);
9+
}
10+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Threading.Tasks;
5+
using Microsoft.Azure.WebJobs.Script.WebHost.Models;
6+
7+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Management.LinuxSpecialization
8+
{
9+
public interface IRunFromPackageHandler
10+
{
11+
Task<string> Download(RunFromPackageContext pkgContext);
12+
13+
Task<bool> MountAzureFileShare(HostAssignmentContext assignmentContext);
14+
15+
Task<bool> ApplyBlobPackageContext(RunFromPackageContext pkgContext, string targetPath, bool azureFilesMounted,
16+
bool throwOnFailure = true);
17+
}
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Management.LinuxSpecialization
5+
{
6+
public interface IUnZipHandler
7+
{
8+
void UnzipPackage(string filePath, string scriptPath);
9+
}
10+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
using Microsoft.Azure.Storage.Blob;
7+
using Microsoft.Extensions.Logging;
8+
9+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Management.LinuxSpecialization
10+
{
11+
public class RunFromPackageCloudBlockBlobService
12+
{
13+
public virtual async Task<bool> BlobExists(string url, string environmentVariableName, ILogger logger)
14+
{
15+
return await BlobExistsAsync(url, environmentVariableName, logger);
16+
}
17+
18+
private static async Task<bool> BlobExistsAsync(string url, string environmentVariableName, ILogger logger)
19+
{
20+
bool exists = false;
21+
await Utility.InvokeWithRetriesAsync(async () =>
22+
{
23+
try
24+
{
25+
CloudBlockBlob blob = new CloudBlockBlob(new Uri(url));
26+
exists = await blob.ExistsAsync();
27+
}
28+
catch (Exception e)
29+
{
30+
logger.LogError(e, $"Failed to check if zip url blob exists");
31+
throw;
32+
}
33+
}, maxRetries: 2, retryInterval: TimeSpan.FromSeconds(0.3));
34+
35+
if (!exists)
36+
{
37+
logger.LogWarning($"{environmentVariableName} points to an empty location. Function app has no content.");
38+
}
39+
40+
return exists;
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)