Skip to content

Commit 2f757de

Browse files
Francisco-GaminoTylerLeonhardt
authored andcommitted
Enable installing function app dependencies via requirements.psd1. (#166)
* Enable installing function app dependencies via requirements.psd1. * Addressing code review comments. * Addressing code review comments.
1 parent bda1f8e commit 2f757de

19 files changed

+922
-79
lines changed

azure-functions-powershell-worker.sln

+39-59
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,39 @@
1-
2-
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.26124.0
5-
MinimumVisualStudioVersion = 15.0.26124.0
6-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8C758288-3909-4CE1-972D-1BE966628D6C}"
7-
EndProject
8-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Functions.PowerShellWorker", "src\Microsoft.Azure.Functions.PowerShellWorker.csproj", "{939262BA-4823-405E-81CD-436C0B77D524}"
9-
EndProject
10-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{12092936-4F2A-4B40-9AF2-56C840D44FEA}"
11-
EndProject
12-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test\Unit", "test\Unit", "{84b1665e-f5a8-4044-81da-611f1e40508f}"
13-
EndProject
14-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Functions.PowerShellWorker.Test", "test\Unit\Microsoft.Azure.Functions.PowerShellWorker.Test.csproj", "{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}"
15-
EndProject
16-
Global
17-
GlobalSection(SolutionConfigurationPlatforms) = preSolution
18-
Debug|Any CPU = Debug|Any CPU
19-
Debug|x64 = Debug|x64
20-
Debug|x86 = Debug|x86
21-
Release|Any CPU = Release|Any CPU
22-
Release|x64 = Release|x64
23-
Release|x86 = Release|x86
24-
EndGlobalSection
25-
GlobalSection(SolutionProperties) = preSolution
26-
HideSolutionNode = FALSE
27-
EndGlobalSection
28-
GlobalSection(ProjectConfigurationPlatforms) = postSolution
29-
{939262BA-4823-405E-81CD-436C0B77D524}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30-
{939262BA-4823-405E-81CD-436C0B77D524}.Debug|Any CPU.Build.0 = Debug|Any CPU
31-
{939262BA-4823-405E-81CD-436C0B77D524}.Debug|x64.ActiveCfg = Debug|Any CPU
32-
{939262BA-4823-405E-81CD-436C0B77D524}.Debug|x64.Build.0 = Debug|Any CPU
33-
{939262BA-4823-405E-81CD-436C0B77D524}.Debug|x86.ActiveCfg = Debug|Any CPU
34-
{939262BA-4823-405E-81CD-436C0B77D524}.Debug|x86.Build.0 = Debug|Any CPU
35-
{939262BA-4823-405E-81CD-436C0B77D524}.Release|Any CPU.ActiveCfg = Release|Any CPU
36-
{939262BA-4823-405E-81CD-436C0B77D524}.Release|Any CPU.Build.0 = Release|Any CPU
37-
{939262BA-4823-405E-81CD-436C0B77D524}.Release|x64.ActiveCfg = Release|Any CPU
38-
{939262BA-4823-405E-81CD-436C0B77D524}.Release|x64.Build.0 = Release|Any CPU
39-
{939262BA-4823-405E-81CD-436C0B77D524}.Release|x86.ActiveCfg = Release|Any CPU
40-
{939262BA-4823-405E-81CD-436C0B77D524}.Release|x86.Build.0 = Release|Any CPU
41-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
42-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
43-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Debug|x64.ActiveCfg = Debug|Any CPU
44-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Debug|x64.Build.0 = Debug|Any CPU
45-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Debug|x86.ActiveCfg = Debug|Any CPU
46-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Debug|x86.Build.0 = Debug|Any CPU
47-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
48-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Release|Any CPU.Build.0 = Release|Any CPU
49-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Release|x64.ActiveCfg = Release|Any CPU
50-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Release|x64.Build.0 = Release|Any CPU
51-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Release|x86.ActiveCfg = Release|Any CPU
52-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4}.Release|x86.Build.0 = Release|Any CPU
53-
EndGlobalSection
54-
GlobalSection(NestedProjects) = preSolution
55-
{939262BA-4823-405E-81CD-436C0B77D524} = {8C758288-3909-4CE1-972D-1BE966628D6C}
56-
{84b1665e-f5a8-4044-81da-611f1e40508f} = {12092936-4F2A-4B40-9AF2-56C840D44FEA}
57-
{535C8DA3-479D-42BF-B1AF-5B03ECAF67A4} = {84b1665e-f5a8-4044-81da-611f1e40508f}
58-
EndGlobalSection
59-
EndGlobal
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio 15
4+
VisualStudioVersion = 15.0.28307.329
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0CB53CC1-0A0D-4F24-8DB1-012B30D1556A}"
7+
EndProject
8+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Functions.PowerShellWorker", "src\Microsoft.Azure.Functions.PowerShellWorker.csproj", "{1BC596F9-5ECD-402E-A438-B9C0B865F498}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{37D1E02C-EC87-4DC9-B808-4D7A57A70468}"
11+
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Functions.PowerShellWorker.Test", "test\Unit\Microsoft.Azure.Functions.PowerShellWorker.Test.csproj", "{5DEF7E51-7E6A-4AEF-9AE1-3092B8E98A0E}"
13+
EndProject
14+
Global
15+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16+
Debug|Any CPU = Debug|Any CPU
17+
Release|Any CPU = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{1BC596F9-5ECD-402E-A438-B9C0B865F498}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{1BC596F9-5ECD-402E-A438-B9C0B865F498}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{1BC596F9-5ECD-402E-A438-B9C0B865F498}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{1BC596F9-5ECD-402E-A438-B9C0B865F498}.Release|Any CPU.Build.0 = Release|Any CPU
24+
{5DEF7E51-7E6A-4AEF-9AE1-3092B8E98A0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{5DEF7E51-7E6A-4AEF-9AE1-3092B8E98A0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{5DEF7E51-7E6A-4AEF-9AE1-3092B8E98A0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{5DEF7E51-7E6A-4AEF-9AE1-3092B8E98A0E}.Release|Any CPU.Build.0 = Release|Any CPU
28+
EndGlobalSection
29+
GlobalSection(SolutionProperties) = preSolution
30+
HideSolutionNode = FALSE
31+
EndGlobalSection
32+
GlobalSection(NestedProjects) = preSolution
33+
{1BC596F9-5ECD-402E-A438-B9C0B865F498} = {0CB53CC1-0A0D-4F24-8DB1-012B30D1556A}
34+
{5DEF7E51-7E6A-4AEF-9AE1-3092B8E98A0E} = {37D1E02C-EC87-4DC9-B808-4D7A57A70468}
35+
EndGlobalSection
36+
GlobalSection(ExtensibilityGlobals) = postSolution
37+
SolutionGuid = {7062139F-0064-4B80-A47E-CB5DD14804C7}
38+
EndGlobalSection
39+
EndGlobal

build.ps1

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ if(!$NoBuild.IsPresent) {
7171

7272
Invoke-PSDepend -Path $requirements -Force
7373

74+
Write-Log "Deleting fullclr folder from PackageManagement module if the folder exists ..."
75+
Get-Item "$PSScriptRoot/src/Modules/PackageManagement/1.1.7.0/fullclr" -ErrorAction SilentlyContinue |
76+
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
77+
7478
# TODO: Remove this once the SDK properly bundles modules
7579
Get-WebFile -Url 'https://raw.githubusercontent.com/PowerShell/PowerShell/master/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1' `
7680
-OutFile "$PSScriptRoot/src/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1"
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement
7+
{
8+
internal class DependencyInfo
9+
{
10+
internal readonly string Name;
11+
internal readonly string MajorVersion;
12+
internal readonly string LatestVersion;
13+
14+
internal DependencyInfo(string name, string majorVersion, string latestVersion)
15+
{
16+
Name = name;
17+
MajorVersion = majorVersion;
18+
LatestVersion = latestVersion;
19+
}
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using System;
7+
8+
namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement
9+
{
10+
internal class DependencyInstallationException : Exception
11+
{
12+
internal DependencyInstallationException()
13+
{
14+
}
15+
internal DependencyInstallationException(string message)
16+
:base(message)
17+
{
18+
}
19+
internal DependencyInstallationException(string message, Exception innException)
20+
: base(message, innException)
21+
{
22+
}
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//
2+
// Copyright (c) Microsoft. All rights reserved.
3+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
4+
//
5+
6+
using System;
7+
using System.Collections.Generic;
8+
using System.IO;
9+
using System.Net;
10+
using System.Xml;
11+
12+
namespace Microsoft.Azure.Functions.PowerShellWorker.DependencyManagement
13+
{
14+
internal class DependencyManagementUtils
15+
{
16+
// The PowerShellGallery uri to query for the latest module version.
17+
private const string PowerShellGalleryFindPackagesByIdUri = "https://www.powershellgallery.com/api/v2/FindPackagesById()?id=";
18+
19+
/// <summary>
20+
/// Deletes the contents at the given directory.
21+
/// </summary>
22+
internal static void EmptyDirectory(string path)
23+
{
24+
try
25+
{
26+
var directoryInfo = new DirectoryInfo(path);
27+
28+
if (directoryInfo.Exists)
29+
{
30+
IEnumerable<string> files = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories);
31+
32+
foreach (var file in files)
33+
{
34+
var fileInfo = new FileInfo(file);
35+
36+
// Remove any problematic file attributes.
37+
fileInfo.Attributes = fileInfo.Attributes &
38+
~(FileAttributes.Hidden | FileAttributes.ReadOnly | FileAttributes.System);
39+
fileInfo.Delete();
40+
}
41+
42+
foreach (DirectoryInfo subDirectory in directoryInfo.GetDirectories())
43+
{
44+
subDirectory.Delete(true);
45+
}
46+
}
47+
}
48+
catch (Exception)
49+
{
50+
var errorMsg = string.Format(PowerShellWorkerStrings.FailToClenupModuleDestinationPath, path);
51+
throw new InvalidOperationException(errorMsg);
52+
}
53+
}
54+
55+
/// <summary>
56+
/// Returns the latest module version from the PSGallery for the given module name and major version.
57+
/// </summary>
58+
internal static string GetModuleLatestSupportedVersion(string moduleName, string majorVersion)
59+
{
60+
Uri address = new Uri($"{PowerShellGalleryFindPackagesByIdUri}'{moduleName}'");
61+
int configuredRetries = 3;
62+
int noOfRetries = 1;
63+
64+
string latestVersionForMajorVersion = null;
65+
66+
while (noOfRetries <= configuredRetries)
67+
{
68+
try
69+
{
70+
HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
71+
using (HttpWebResponse response = request?.GetResponse() as HttpWebResponse)
72+
{
73+
if (response != null)
74+
{
75+
// Load up the XML response
76+
XmlDocument doc = new XmlDocument();
77+
using (XmlReader reader = XmlReader.Create(response.GetResponseStream()))
78+
{
79+
doc.Load(reader);
80+
}
81+
82+
// Add the namespaces for the gallery xml content
83+
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
84+
nsmgr.AddNamespace("ps", "http://www.w3.org/2005/Atom");
85+
nsmgr.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices");
86+
nsmgr.AddNamespace("m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata");
87+
88+
// Find the version information
89+
XmlNode root = doc.DocumentElement;
90+
var props = root.SelectNodes("//m:properties/d:Version", nsmgr);
91+
if (props != null && props.Count > 0)
92+
{
93+
for (int i = 0; i < props.Count; i++)
94+
{
95+
if (props[i].FirstChild.Value.StartsWith(majorVersion))
96+
{
97+
latestVersionForMajorVersion = props[i].FirstChild.Value;
98+
}
99+
}
100+
}
101+
break;
102+
}
103+
}
104+
}
105+
catch (Exception ex)
106+
{
107+
WebException webEx = ex as WebException;
108+
if (webEx == null || noOfRetries >= configuredRetries)
109+
{
110+
throw;
111+
}
112+
113+
// Only retry the web exception
114+
if (ShouldRetry(webEx))
115+
{
116+
noOfRetries++;
117+
}
118+
}
119+
}
120+
121+
// If we could not find the latest module version error out.
122+
if (string.IsNullOrEmpty(latestVersionForMajorVersion))
123+
{
124+
var errorMsg = string.Format(PowerShellWorkerStrings.CannotFindModuleVersion, moduleName, majorVersion);
125+
var argException = new ArgumentException(errorMsg);
126+
throw argException;
127+
}
128+
129+
return latestVersionForMajorVersion;
130+
}
131+
132+
/// <summary>
133+
/// Returns true if the given WebException status matches one of the following:
134+
/// SendFailure, ConnectFailure, UnknownError or Timeout.
135+
/// </summary>
136+
private static bool ShouldRetry(WebException webEx)
137+
{
138+
if (webEx == null)
139+
{
140+
return false;
141+
}
142+
143+
if (webEx.Status == WebExceptionStatus.SendFailure ||
144+
webEx.Status == WebExceptionStatus.ConnectFailure ||
145+
webEx.Status == WebExceptionStatus.UnknownError ||
146+
webEx.Status == WebExceptionStatus.Timeout)
147+
{
148+
return true;
149+
}
150+
151+
return false;
152+
}
153+
}
154+
}

0 commit comments

Comments
 (0)