Allow building an pushing site extension for azure functional testing (#87)

This commit is contained in:
Pavel Krymets 2017-08-23 14:55:27 -07:00 committed by GitHub
parent f4ee5bcf04
commit 9778c29007
20 changed files with 1452 additions and 1 deletions

2
.gitignore vendored
View File

@ -30,3 +30,5 @@ project.lock.json
.testPublish/
global.json
korebuild-lock.txt
msbuild.binlog
.test-dotnet

View File

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26412.1
VisualStudioVersion = 15.0.26814.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureAppServicesIntegration", "src\Microsoft.AspNetCore.AzureAppServicesIntegration\Microsoft.AspNetCore.AzureAppServicesIntegration.csproj", "{5916BEB5-0969-469B-976C-A392E015DFAC}"
EndProject
@ -35,6 +35,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureA
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationInsights.HostingStartup.Tests", "test\ApplicationInsights.HostingStartup.Tests\ApplicationInsights.HostingStartup.Tests.csproj", "{0899A101-E451-40A4-81B0-7AA18202C25D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureAppServices.FunctionalTests", "test\Microsoft.AspNetCore.AzureAppServices.FunctionalTests\Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj", "{2B2C37FF-9249-4EA4-9A7F-038B55A15C2C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.AzureAppServices.TestBundle", "src\Microsoft.AspNetCore.AzureAppServices.TestBundle\Microsoft.AspNetCore.AzureAppServices.TestBundle.csproj", "{1EC31DA1-131D-4257-B001-BE8391E6077E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -81,6 +85,14 @@ Global
{0899A101-E451-40A4-81B0-7AA18202C25D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0899A101-E451-40A4-81B0-7AA18202C25D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0899A101-E451-40A4-81B0-7AA18202C25D}.Release|Any CPU.Build.0 = Release|Any CPU
{2B2C37FF-9249-4EA4-9A7F-038B55A15C2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B2C37FF-9249-4EA4-9A7F-038B55A15C2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B2C37FF-9249-4EA4-9A7F-038B55A15C2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B2C37FF-9249-4EA4-9A7F-038B55A15C2C}.Release|Any CPU.Build.0 = Release|Any CPU
{1EC31DA1-131D-4257-B001-BE8391E6077E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1EC31DA1-131D-4257-B001-BE8391E6077E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1EC31DA1-131D-4257-B001-BE8391E6077E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1EC31DA1-131D-4257-B001-BE8391E6077E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -96,5 +108,10 @@ Global
{9B22E525-FEC9-4C7C-9F9C-598C15BD0250} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435}
{1CE2D76B-39E6-46C0-8F6F-C63E370955A9} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435}
{0899A101-E451-40A4-81B0-7AA18202C25D} = {CD650B4B-81C2-4A44-AEF2-A251A877C1F0}
{2B2C37FF-9249-4EA4-9A7F-038B55A15C2C} = {CD650B4B-81C2-4A44-AEF2-A251A877C1F0}
{1EC31DA1-131D-4257-B001-BE8391E6077E} = {FF9B744E-6C59-40CC-9E41-9D2EBD292435}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5743DFE7-1AA5-439D-84AE-A480EA389927}
EndGlobalSection
EndGlobal

View File

@ -5,11 +5,13 @@
<ApplicationInsightsVersion>2.1.1</ApplicationInsightsVersion>
<InternalAspNetCoreSdkVersion>2.1.1-*</InternalAspNetCoreSdkVersion>
<MoqVersion>4.7.49</MoqVersion>
<MicrosoftAzureManagementFluentVersion>1.1.3</MicrosoftAzureManagementFluentVersion>
<NETStandardImplicitPackageVersion>2.0.0-*</NETStandardImplicitPackageVersion>
<NETStandardLibraryNETFrameworkVersion>2.0.0-*</NETStandardLibraryNETFrameworkVersion>
<RuntimeFrameworkVersion Condition="'$(TargetFramework)'=='netcoreapp2.0'">2.0.0-*</RuntimeFrameworkVersion>
<TestSdkVersion>15.3.0</TestSdkVersion>
<XdtVersion>1.4.0</XdtVersion>
<XunitVersion>2.3.0-beta4-build3742</XunitVersion>
<WindowsAzureStorageVersion>8.3.0</WindowsAzureStorageVersion>
</PropertyGroup>
</Project>

2
build/dotnet-install.cmd Normal file
View File

@ -0,0 +1,2 @@
@ECHO OFF
PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "& '%~dp0dotnet-install.ps1' %*; exit $LASTEXITCODE"

503
build/dotnet-install.ps1 vendored Normal file
View File

@ -0,0 +1,503 @@
#
# Copyright (c) .NET Foundation and contributors. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#
<#
.SYNOPSIS
Installs dotnet cli
.DESCRIPTION
Installs dotnet cli. If dotnet installation already exists in the given directory
it will update it only if the requested version differs from the one already installed.
.PARAMETER Channel
Default: LTS
Download from the Channel specified. Possible values:
- Current - most current release
- LTS - most current supported release
- 2-part version in a format A.B - represents a specific release
examples: 2.0; 1.0
- Branch name
examples: release/2.0.0; Master
.PARAMETER Version
Default: latest
Represents a build version on specific channel. Possible values:
- latest - most latest build on specific channel
- coherent - most latest coherent build on specific channel
coherent applies only to SDK downloads
- 3-part version in a format A.B.C - represents specific version of build
examples: 2.0.0-preview2-006120; 1.1.0
.PARAMETER InstallDir
Default: %LocalAppData%\Microsoft\dotnet
Path to where to install dotnet. Note that binaries will be placed directly in a given directory.
.PARAMETER Architecture
Default: <auto> - this value represents currently running OS architecture
Architecture of dotnet binaries to be installed.
Possible values are: <auto>, x64 and x86
.PARAMETER SharedRuntime
Default: false
Installs just the shared runtime bits, not the entire SDK
.PARAMETER DryRun
If set it will not perform installation but instead display what command line to use to consistently install
currently requested version of dotnet cli. In example if you specify version 'latest' it will display a link
with specific version so that this command can be used deterministicly in a build script.
It also displays binaries location if you prefer to install or download it yourself.
.PARAMETER NoPath
By default this script will set environment variable PATH for the current process to the binaries folder inside installation folder.
If set it will display binaries location but not set any environment variable.
.PARAMETER Verbose
Displays diagnostics information.
.PARAMETER AzureFeed
Default: https://dotnetcli.azureedge.net/dotnet
This parameter typically is not changed by the user.
It allows to change URL for the Azure feed used by this installer.
.PARAMETER UncachedFeed
This parameter typically is not changed by the user.
It allows to change URL for the Uncached feed used by this installer.
.PARAMETER ProxyAddress
If set, the installer will use the proxy when making web requests
.PARAMETER ProxyUseDefaultCredentials
Default: false
Use default credentials, when using proxy address.
#>
[cmdletbinding()]
param(
[string]$Channel="LTS",
[string]$Version="Latest",
[string]$InstallDir="<auto>",
[string]$Architecture="<auto>",
[switch]$SharedRuntime,
[switch]$DryRun,
[switch]$NoPath,
[string]$AzureFeed="https://dotnetcli.azureedge.net/dotnet",
[string]$UncachedFeed="https://dotnetcli.blob.core.windows.net/dotnet",
[string]$ProxyAddress,
[switch]$ProxyUseDefaultCredentials
)
Set-StrictMode -Version Latest
$ErrorActionPreference="Stop"
$ProgressPreference="SilentlyContinue"
$BinFolderRelativePath=""
# example path with regex: shared/1.0.0-beta-12345/somepath
$VersionRegEx="/\d+\.\d+[^/]+/"
$OverrideNonVersionedFiles=$true
function Say($str) {
Write-Output "dotnet-install: $str"
}
function Say-Verbose($str) {
Write-Verbose "dotnet-install: $str"
}
function Say-Invocation($Invocation) {
$command = $Invocation.MyCommand;
$args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ")
Say-Verbose "$command $args"
}
function Invoke-With-Retry([ScriptBlock]$ScriptBlock, [int]$MaxAttempts = 3, [int]$SecondsBetweenAttempts = 1) {
$Attempts = 0
while ($true) {
try {
return $ScriptBlock.Invoke()
}
catch {
$Attempts++
if ($Attempts -lt $MaxAttempts) {
Start-Sleep $SecondsBetweenAttempts
}
else {
throw
}
}
}
}
function Get-Machine-Architecture() {
Say-Invocation $MyInvocation
# possible values: AMD64, IA64, x86
return $ENV:PROCESSOR_ARCHITECTURE
}
# TODO: Architecture and CLIArchitecture should be unified
function Get-CLIArchitecture-From-Architecture([string]$Architecture) {
Say-Invocation $MyInvocation
switch ($Architecture.ToLower()) {
{ $_ -eq "<auto>" } { return Get-CLIArchitecture-From-Architecture $(Get-Machine-Architecture) }
{ ($_ -eq "amd64") -or ($_ -eq "x64") } { return "x64" }
{ $_ -eq "x86" } { return "x86" }
default { throw "Architecture not supported. If you think this is a bug, please report it at https://github.com/dotnet/cli/issues" }
}
}
function Get-Version-Info-From-Version-Text([string]$VersionText) {
Say-Invocation $MyInvocation
$Data = @($VersionText.Split([char[]]@(), [StringSplitOptions]::RemoveEmptyEntries));
$VersionInfo = @{}
$VersionInfo.CommitHash = $Data[0].Trim()
$VersionInfo.Version = $Data[1].Trim()
return $VersionInfo
}
function Load-Assembly([string] $Assembly) {
try {
Add-Type -Assembly $Assembly | Out-Null
}
catch {
# On Nano Server, Powershell Core Edition is used. Add-Type is unable to resolve base class assemblies because they are not GAC'd.
# Loading the base class assemblies is not unnecessary as the types will automatically get resolved.
}
}
function GetHTTPResponse([Uri] $Uri)
{
Invoke-With-Retry(
{
$HttpClient = $null
try {
# HttpClient is used vs Invoke-WebRequest in order to support Nano Server which doesn't support the Invoke-WebRequest cmdlet.
Load-Assembly -Assembly System.Net.Http
if(-not $ProxyAddress)
{
# Despite no proxy being explicitly specified, we may still be behind a default proxy
$DefaultProxy = [System.Net.WebRequest]::DefaultWebProxy;
if($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))){
$ProxyAddress = $DefaultProxy.GetProxy($Uri).OriginalString
$ProxyUseDefaultCredentials = $true
}
}
if($ProxyAddress){
$HttpClientHandler = New-Object System.Net.Http.HttpClientHandler
$HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{Address=$ProxyAddress;UseDefaultCredentials=$ProxyUseDefaultCredentials}
$HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler
}
else {
$HttpClient = New-Object System.Net.Http.HttpClient
}
# Default timeout for HttpClient is 100s. For a 50 MB download this assumes 500 KB/s average, any less will time out
# 10 minutes allows it to work over much slower connections.
$HttpClient.Timeout = New-TimeSpan -Minutes 10
$Response = $HttpClient.GetAsync($Uri).Result
if (($Response -eq $null) -or (-not ($Response.IsSuccessStatusCode)))
{
$ErrorMsg = "Failed to download $Uri."
if ($Response -ne $null)
{
$ErrorMsg += " $Response"
}
throw $ErrorMsg
}
return $Response
}
finally {
if ($HttpClient -ne $null) {
$HttpClient.Dispose()
}
}
})
}
function Get-Latest-Version-Info([string]$AzureFeed, [string]$Channel, [bool]$Coherent) {
Say-Invocation $MyInvocation
$VersionFileUrl = $null
if ($SharedRuntime) {
$VersionFileUrl = "$UncachedFeed/Runtime/$Channel/latest.version"
}
else {
if ($Coherent) {
$VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.coherent.version"
}
else {
$VersionFileUrl = "$UncachedFeed/Sdk/$Channel/latest.version"
}
}
$Response = GetHTTPResponse -Uri $VersionFileUrl
$StringContent = $Response.Content.ReadAsStringAsync().Result
switch ($Response.Content.Headers.ContentType) {
{ ($_ -eq "application/octet-stream") } { $VersionText = [Text.Encoding]::UTF8.GetString($StringContent) }
{ ($_ -eq "text/plain") } { $VersionText = $StringContent }
{ ($_ -eq "text/plain; charset=UTF-8") } { $VersionText = $StringContent }
default { throw "``$Response.Content.Headers.ContentType`` is an unknown .version file content type." }
}
$VersionInfo = Get-Version-Info-From-Version-Text $VersionText
return $VersionInfo
}
function Get-Specific-Version-From-Version([string]$AzureFeed, [string]$Channel, [string]$Version) {
Say-Invocation $MyInvocation
switch ($Version.ToLower()) {
{ $_ -eq "latest" } {
$LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $False
return $LatestVersionInfo.Version
}
{ $_ -eq "coherent" } {
$LatestVersionInfo = Get-Latest-Version-Info -AzureFeed $AzureFeed -Channel $Channel -Coherent $True
return $LatestVersionInfo.Version
}
default { return $Version }
}
}
function Get-Download-Link([string]$AzureFeed, [string]$Channel, [string]$SpecificVersion, [string]$CLIArchitecture) {
Say-Invocation $MyInvocation
if ($SharedRuntime) {
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-runtime-$SpecificVersion-win-$CLIArchitecture.zip"
}
else {
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-sdk-$SpecificVersion-win-$CLIArchitecture.zip"
}
Say-Verbose "Constructed primary payload URL: $PayloadURL"
return $PayloadURL
}
function Get-LegacyDownload-Link([string]$AzureFeed, [string]$Channel, [string]$SpecificVersion, [string]$CLIArchitecture) {
Say-Invocation $MyInvocation
if ($SharedRuntime) {
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/dotnet-win-$CLIArchitecture.$SpecificVersion.zip"
}
else {
$PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-dev-win-$CLIArchitecture.$SpecificVersion.zip"
}
Say-Verbose "Constructed legacy payload URL: $PayloadURL"
return $PayloadURL
}
function Get-User-Share-Path() {
Say-Invocation $MyInvocation
$InstallRoot = $env:DOTNET_INSTALL_DIR
if (!$InstallRoot) {
$InstallRoot = "$env:LocalAppData\Microsoft\dotnet"
}
return $InstallRoot
}
function Resolve-Installation-Path([string]$InstallDir) {
Say-Invocation $MyInvocation
if ($InstallDir -eq "<auto>") {
return Get-User-Share-Path
}
return $InstallDir
}
function Get-Version-Info-From-Version-File([string]$InstallRoot, [string]$RelativePathToVersionFile) {
Say-Invocation $MyInvocation
$VersionFile = Join-Path -Path $InstallRoot -ChildPath $RelativePathToVersionFile
Say-Verbose "Local version file: $VersionFile"
if (Test-Path $VersionFile) {
$VersionText = cat $VersionFile
Say-Verbose "Local version file text: $VersionText"
return Get-Version-Info-From-Version-Text $VersionText
}
Say-Verbose "Local version file not found."
return $null
}
function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) {
Say-Invocation $MyInvocation
$DotnetPackagePath = Join-Path -Path $InstallRoot -ChildPath $RelativePathToPackage | Join-Path -ChildPath $SpecificVersion
Say-Verbose "Is-Dotnet-Package-Installed: Path to a package: $DotnetPackagePath"
return Test-Path $DotnetPackagePath -PathType Container
}
function Get-Absolute-Path([string]$RelativeOrAbsolutePath) {
# Too much spam
# Say-Invocation $MyInvocation
return $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($RelativeOrAbsolutePath)
}
function Get-Path-Prefix-With-Version($path) {
$match = [regex]::match($path, $VersionRegEx)
if ($match.Success) {
return $entry.FullName.Substring(0, $match.Index + $match.Length)
}
return $null
}
function Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package([System.IO.Compression.ZipArchive]$Zip, [string]$OutPath) {
Say-Invocation $MyInvocation
$ret = @()
foreach ($entry in $Zip.Entries) {
$dir = Get-Path-Prefix-With-Version $entry.FullName
if ($dir -ne $null) {
$path = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $dir)
if (-Not (Test-Path $path -PathType Container)) {
$ret += $dir
}
}
}
$ret = $ret | Sort-Object | Get-Unique
$values = ($ret | foreach { "$_" }) -join ";"
Say-Verbose "Directories to unpack: $values"
return $ret
}
# Example zip content and extraction algorithm:
# Rule: files if extracted are always being extracted to the same relative path locally
# .\
# a.exe # file does not exist locally, extract
# b.dll # file exists locally, override only if $OverrideFiles set
# aaa\ # same rules as for files
# ...
# abc\1.0.0\ # directory contains version and exists locally
# ... # do not extract content under versioned part
# abc\asd\ # same rules as for files
# ...
# def\ghi\1.0.1\ # directory contains version and does not exist locally
# ... # extract content
function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) {
Say-Invocation $MyInvocation
Load-Assembly -Assembly System.IO.Compression.FileSystem
Set-Variable -Name Zip
try {
$Zip = [System.IO.Compression.ZipFile]::OpenRead($ZipPath)
$DirectoriesToUnpack = Get-List-Of-Directories-And-Versions-To-Unpack-From-Dotnet-Package -Zip $Zip -OutPath $OutPath
foreach ($entry in $Zip.Entries) {
$PathWithVersion = Get-Path-Prefix-With-Version $entry.FullName
if (($PathWithVersion -eq $null) -Or ($DirectoriesToUnpack -contains $PathWithVersion)) {
$DestinationPath = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $entry.FullName)
$DestinationDir = Split-Path -Parent $DestinationPath
$OverrideFiles=$OverrideNonVersionedFiles -Or (-Not (Test-Path $DestinationPath))
if ((-Not $DestinationPath.EndsWith("\")) -And $OverrideFiles) {
New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $DestinationPath, $OverrideNonVersionedFiles)
}
}
}
}
finally {
if ($Zip -ne $null) {
$Zip.Dispose()
}
}
}
function DownloadFile([Uri]$Uri, [string]$OutPath) {
$Stream = $null
try {
$Response = GetHTTPResponse -Uri $Uri
$Stream = $Response.Content.ReadAsStreamAsync().Result
$File = [System.IO.File]::Create($OutPath)
$Stream.CopyTo($File)
$File.Close()
}
finally {
if ($Stream -ne $null) {
$Stream.Dispose()
}
}
}
function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolderRelativePath) {
$BinPath = Get-Absolute-Path $(Join-Path -Path $InstallRoot -ChildPath $BinFolderRelativePath)
if (-Not $NoPath) {
Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process."
$env:path = "$BinPath;" + $env:path
}
else {
Say "Binaries of dotnet can be found in $BinPath"
}
}
$CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture
$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version
$DownloadLink = Get-Download-Link -AzureFeed $AzureFeed -Channel $Channel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
$LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -Channel $Channel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
if ($DryRun) {
Say "Payload URLs:"
Say "Primary - $DownloadLink"
Say "Legacy - $LegacyDownloadLink"
Say "Repeatable invocation: .\$($MyInvocation.MyCommand) -Version $SpecificVersion -Channel $Channel -Architecture $CLIArchitecture -InstallDir $InstallDir"
exit 0
}
$InstallRoot = Resolve-Installation-Path $InstallDir
Say-Verbose "InstallRoot: $InstallRoot"
$IsSdkInstalled = Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage "sdk" -SpecificVersion $SpecificVersion
Say-Verbose ".NET SDK installed? $IsSdkInstalled"
if ($IsSdkInstalled) {
Say ".NET SDK version $SpecificVersion is already installed."
Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath
exit 0
}
New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
$installDrive = $((Get-Item $InstallRoot).PSDrive.Name);
Write-Output "${installDrive}:";
$free = Get-CimInstance -Class win32_logicaldisk | where Deviceid -eq "${installDrive}:"
if ($free.Freespace / 1MB -le 100 ) {
Say "There is not enough disk space on drive ${installDrive}:"
exit 0
}
$ZipPath = [System.IO.Path]::GetTempFileName()
Say-Verbose "Zip path: $ZipPath"
Say "Downloading link: $DownloadLink"
try {
DownloadFile -Uri $DownloadLink -OutPath $ZipPath
}
catch {
Say "Cannot download: $DownloadLink"
$DownloadLink = $LegacyDownloadLink
$ZipPath = [System.IO.Path]::GetTempFileName()
Say-Verbose "Legacy zip path: $ZipPath"
Say "Downloading legacy link: $DownloadLink"
DownloadFile -Uri $DownloadLink -OutPath $ZipPath
}
Say "Extracting zip from $DownloadLink"
Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot
Remove-Item $ZipPath
Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot -BinFolderRelativePath $BinFolderRelativePath
Say "Installation finished"
exit 0

17
build/repo.props Normal file
View File

@ -0,0 +1,17 @@
<Project>
<PropertyGroup>
<FunctionalTestsProject>$(RepositoryRoot)test\Microsoft.AspNetCore.AzureAppServices.FunctionalTests\Microsoft.AspNetCore.AzureAppServices.FunctionalTests.csproj</FunctionalTestsProject>
</PropertyGroup>
<ItemGroup>
<ExcludeFromPack Include="$(RepositoryRoot)src\Microsoft.AspNetCore.AzureAppServices.TestBundle\Microsoft.AspNetCore.AzureAppServices.TestBundle.csproj" />
<ExcludeFromTest
Condition="'$(AntaresTests)' != ''"
Include="$(RepositoryRoot)test\*\*.csproj"
Exclude="$(FunctionalTestsProject)" />
<ExcludeFromTest
Condition="'$(AntaresTests)' == ''"
Include="$(FunctionalTestsProject)" />
</ItemGroup>
</Project>

39
build/repo.targets Normal file
View File

@ -0,0 +1,39 @@
<Project>
<PropertyGroup>
<SiteExtensionProjectDirectory>$(RepositoryRoot)src\Microsoft.AspNetCore.AzureAppServices.TestBundle\</SiteExtensionProjectDirectory>
<PublishFeed Condition="$(PublishFeed) == ''">https://dotnet.myget.org/F/aspnetcore-ci-dev/</PublishFeed>
<DotnetChannel>master</DotnetChannel>
<DotnetVersion>coherent</DotnetVersion>
</PropertyGroup>
<Target Name="BuildSiteExtension">
<PropertyGroup>
<SiteExtensionWorkingDirectory>$(RepositoryRoot).test-dotnet\</SiteExtensionWorkingDirectory>
<CliVersionRelativePath>build\dotnet.version</CliVersionRelativePath>
</PropertyGroup>
<RemoveDir Directories="$(SiteExtensionWorkingDirectory)" />
<Exec Command="$(MSBuildThisFileDirectory)\dotnet-install.cmd -Channel $(DotnetChannel) -Version $(DotnetVersion) -Architecture x86 -InstallDir $(SiteExtensionWorkingDirectory) -NoPath" />
<MSBuild Projects="$(SiteExtensionProjectDirectory)Microsoft.AspNetCore.AzureAppServices.TestBundle.csproj"
Targets="Restore;Pack"
Properties="DotnetHomeDirectory=$(SiteExtensionWorkingDirectory)" />
<Exec Command="$(SiteExtensionWorkingDirectory)dotnet --version > $(RepositoryRoot)artifacts\$(CliVersionRelativePath)" />
</Target>
<Target Name="PushSiteExtension">
<ItemGroup>
<RepositoryNupkgs Include="$(SiteExtensionProjectDirectory)bin\$(Configuration)\*.nupkg" />
</ItemGroup>
<PushNuGetPackages
Packages="@(RepositoryNupkgs)"
Feed="$(PublishFeed)"
ApiKey="$(APIKey)"
Timeout="6000"/>
</Target>
</Project>

View File

@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<Title>ASP.NET Core Test Bundle Extensions</Title>
<Description>This extension enables testing functionality of ASP.NET Core on Azure WebSites.</Description>
<TargetFramework>net461</TargetFramework>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<PackageTags>aspnet;logging;aspnetcore;AzureSiteExtension</PackageTags>
<PackageType>AzureSiteExtension</PackageType>
<NoPackageAnalysis>true</NoPackageAnalysis>
<IncludeBuildOutput>false</IncludeBuildOutput>
<IncludeSymbols>false</IncludeSymbols>
<IncludeSource>false</IncludeSource>
<ContentTargetFolders>content</ContentTargetFolders>
<PackageId>AspNetCoreTestBundle</PackageId>
<!-- These need to be set manually because this is not marked as a shipping (to nuget.org) package. https://github.com/aspnet/AzureIntegration/issues/38 -->
<PackageLicenseUrl>https://github.com/aspnet/AzureIntegration/blob/rel/2.0.0-preview1/LICENSE.txt</PackageLicenseUrl>
<PackageIconUrl>https://go.microsoft.com/fwlink/?LinkID=288859</PackageIconUrl>
<PackageProjectUrl>https://www.asp.net/</PackageProjectUrl>
</PropertyGroup>
<ItemGroup>
<Content Include="applicationHost.xdt" />
<Content Include="$(OutputPath)\Microsoft.Web.Xdt.Extensions.dll" PackagePath="content" />
<Content Include="$(DotnetHomeDirectory)\**\*.*" Exclude="$(DotnetHomeDirectory)\**\nuGetPackagesArchive.lzma" Condition="$(DotnetHomeDirectory) != ''" PackagePath="content\%(RecursiveDir)%(FileName)%(Extension)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.Web.Xdt.Extensions\Microsoft.Web.Xdt.Extensions.csproj" PrivateAssets="All" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<xdt:Import path="%XDT_EXTENSIONPATH%\Microsoft.Web.Xdt.Extensions.dll"
namespace="Microsoft.Web.Xdt.Extensions" />
<system.webServer xdt:Transform="InsertIfMissing">
<runtime xdt:Transform="InsertIfMissing" >
<environmentVariables xdt:Transform="InsertIfMissing">
<add name="PATH" value="%XDT_EXTENSIONPATH%" xdt:Locator="Match(name)" xdt:Transform="InsertOrAppendAttribute(Attribute='value')" />
</environmentVariables>
</runtime>
</system.webServer>
</configuration>

View File

@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Xunit;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
[CollectionDefinition("Azure")]
public class AzureCollection : ICollectionFixture<AzureFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
}

View File

@ -0,0 +1,154 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.Azure.Management.AppService.Fluent;
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
using Microsoft.Azure.Management.ResourceManager.Fluent.Models;
using Microsoft.Azure.Management.Storage.Fluent;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Rest;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
public class AzureFixture : IDisposable
{
public string Timestamp { get; set; }
public AzureFixture()
{
TestLog = AssemblyTestLog.ForAssembly(typeof(AzureFixture).Assembly);
// TODO: Temporary to see if it's useful and worth exposing
var globalLoggerFactory =
(ILoggerFactory) TestLog.GetType().GetField("_globalLoggerFactory", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(TestLog);
var logger = globalLoggerFactory.CreateLogger<AzureFixture>();
ServiceClientTracing.IsEnabled = true;
ServiceClientTracing.AddTracingInterceptor(new LoggingInterceptor(globalLoggerFactory.CreateLogger(nameof(ServiceClientTracing))));
var clientId = GetRequiredEnvironmentVariable("AZURE_AUTH_CLIENT_ID");
var clientSecret = GetRequiredEnvironmentVariable("AZURE_AUTH_CLIENT_SECRET");
var tenant = GetRequiredEnvironmentVariable("AZURE_AUTH_TENANT");
var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal(clientId, clientSecret, tenant, AzureEnvironment.AzureGlobalCloud);
Azure = Microsoft.Azure.Management.Fluent.Azure.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(credentials)
.WithDefaultSubscription();
Timestamp = DateTime.Now.ToString("yyyyMMddhhmmss");
var testRunName = GetTimestampedName("FunctionalTests");
logger.LogInformation("Creating resource group {TestRunName}", testRunName);
ResourceGroup = Azure.ResourceGroups
.Define(testRunName)
.WithRegion(Region.USWest2)
.Create();
var servicePlanName = GetTimestampedName("TestPlan");
logger.LogInformation("Creating service plan {servicePlanName}", testRunName);
Plan = Azure.AppServices.AppServicePlans.Define(servicePlanName)
.WithRegion(Region.USWest2)
.WithExistingResourceGroup(ResourceGroup)
.WithFreePricingTier()
.Create();
}
private static string GetRequiredEnvironmentVariable(string name)
{
var authFile = Environment.GetEnvironmentVariable(name);
if (string.IsNullOrEmpty(authFile))
{
throw new InvalidOperationException($"{name} environment variable has to be set to run these tests.");
}
return authFile;
}
public IAppServicePlan Plan { get; set; }
public IStorageAccount DeploymentStorageAccount { get; set; }
public AssemblyTestLog TestLog { get; set; }
public bool DeleteResourceGroup { get; set; } = true;
public IResourceGroup ResourceGroup { get; set; }
public IAzure Azure { get; set; }
public string GetTimestampedName(string name)
{
return name + Timestamp;
}
public async Task<IWebApp> Deploy(string template, IDictionary<string, string> additionalArguments = null, [CallerMemberName] string baseName = null)
{
var siteName = GetTimestampedName(baseName);
var parameters = new Dictionary<string, string>
{
{"siteName", siteName},
{"hostingPlanName", Plan.Name},
{"resourceGroupName", ResourceGroup.Name},
};
foreach (var pair in additionalArguments ?? Enumerable.Empty<KeyValuePair<string, string>>())
{
parameters[pair.Key] = pair.Value;
}
var readAllText = File.ReadAllText(template);
var deployment = await Azure.Deployments.Define(GetTimestampedName("Deployment"))
.WithExistingResourceGroup(ResourceGroup)
.WithTemplate(readAllText)
.WithParameters(ToParametersObject(parameters))
.WithMode(DeploymentMode.Incremental)
.CreateAsync();
deployment = await deployment.RefreshAsync();
var outputs = (JObject)deployment.Outputs;
var siteIdOutput = outputs["siteId"];
if (siteIdOutput == null)
{
throw new InvalidOperationException("Deployment was expected to have 'siteId' output parameter");
}
var siteId = siteIdOutput["value"].Value<string>();
return await Azure.AppServices.WebApps.GetByIdAsync(siteId);
}
private JObject ToParametersObject(Dictionary<string, string> parameters)
{
return new JObject(
parameters.Select(parameter =>
new JProperty(
parameter.Key,
new JObject(
new JProperty("value", parameter.Value)))));
}
public void Dispose()
{
TestLog.Dispose();
if (DeleteResourceGroup && ResourceGroup != null)
{
Azure.ResourceGroups.DeleteByName(ResourceGroup.Name);
}
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Diagnostics;
using Xunit;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
public struct CommandResult
{
public static readonly CommandResult Empty = new CommandResult();
public ProcessStartInfo StartInfo { get; }
public int ExitCode { get; }
public string StdOut { get; }
public string StdErr { get; }
public CommandResult(ProcessStartInfo startInfo, int exitCode, string stdOut, string stdErr)
{
StartInfo = startInfo;
ExitCode = exitCode;
StdOut = stdOut;
StdErr = stdErr;
}
public void AssertSuccess()
{
Assert.True(0 == ExitCode, StdOut + Environment.NewLine + StdErr + Environment.NewLine);
}
}
}

View File

@ -0,0 +1,47 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Rest;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
public class LoggingInterceptor : IServiceClientTracingInterceptor
{
private readonly ILogger _logger;
public LoggingInterceptor(ILogger logger)
{
_logger = logger;
}
public void Information(string message)
{
_logger.LogInformation(message);
}
public void TraceError(string invocationId, Exception exception)
{
_logger.LogInformation(exception, "Exception in {invocationId}", invocationId);
}
public void ReceiveResponse(string invocationId, HttpResponseMessage response)
{
_logger.LogInformation(response.AsFormattedString());
}
public void SendRequest(string invocationId, HttpRequestMessage request)
{
_logger.LogInformation(request.AsFormattedString());
}
public void Configuration(string source, string name, string value) { }
public void EnterMethod(string invocationId, object instance, string method, IDictionary<string, object> parameters) { }
public void ExitMethod(string invocationId, object returnValue) { }
}
}

View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<TargetFrameworks>net461</TargetFrameworks>
<LangVersion>7.1</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Include="Templates\*.*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(AspNetIntegrationTestingVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="Microsoft.Azure.Management.Fluent" Version="$(MicrosoftAzureManagementFluentVersion)" />
<PackageReference Include="WindowsAzure.Storage" Version="$(WindowsAzureStorageVersion)" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,81 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.Azure.Management.AppService.Fluent;
using Microsoft.Azure.Management.AppService.Fluent.Models;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
[Collection("Azure")]
public class TemplateFunctionalTests
{
readonly AzureFixture _fixture;
private readonly ITestOutputHelper _outputHelper;
public TemplateFunctionalTests(AzureFixture fixture, ITestOutputHelper outputHelper)
{
_fixture = fixture;
_outputHelper = outputHelper;
}
[Fact]
public async Task DotnetNewWebRunsInWebApp()
{
using (var logger = GetLogger())
{
Assert.NotNull(_fixture.Azure);
var site = await _fixture.Deploy("Templates\\BasicAppServices.json", null);
var testDirectory = GetTestDirectory();
var dotnet = DotNet(logger, testDirectory);
var result = await dotnet.ExecuteAsync("new web");
result.AssertSuccess();
await site.BuildPublishProfileAsync(testDirectory.FullName);
result = await dotnet.ExecuteAsync("publish /p:PublishProfile=Profile");
result.AssertSuccess();
using (var httpClient = site.CreateClient())
{
var getResult = await httpClient.GetAsync("/");
getResult.EnsureSuccessStatusCode();
Assert.Equal("Hello World!", await getResult.Content.ReadAsStringAsync());
}
}
}
private TestLogger GetLogger([CallerMemberName] string callerName = null)
{
_fixture.TestLog.StartTestLog(_outputHelper, nameof(TemplateFunctionalTests), out var factory, callerName);
return new TestLogger(factory, factory.CreateLogger(callerName));
}
private TestCommand DotNet(TestLogger logger, DirectoryInfo workingDirectory)
{
return new TestCommand("dotnet")
{
Logger = logger,
WorkingDirectory = workingDirectory.FullName
};
}
private DirectoryInfo GetTestDirectory([CallerMemberName] string callerName = null)
{
if (Directory.Exists(callerName))
{
Directory.Delete(callerName, recursive:true);
}
return Directory.CreateDirectory(callerName);
}
}
}

View File

@ -0,0 +1,56 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"siteName": {
"type": "string"
},
"hostingPlanName": {
"type": "string"
},
"resourceGroupName": {
"type": "string"
},
"extensionFeed": {
"type": "string"
},
"extensionName": {
"type": "string"
},
"extensionVersion": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "2015-08-01",
"name": "[parameters('siteName')]",
"type": "Microsoft.Web/sites",
"location": "West US 2",
"properties": {
"serverFarmId": "[resourceId(parameters('resourceGroupName'), 'Microsoft.Web/serverFarms', parameters('hostingPlanName'))]"
},
"resources": [
{
"type": "extensions",
"name": "[parameters('extensionName')]",
"apiVersion": "2015-08-01",
"location": "West US 2",
"properties": {
"version": "[parameters('extensionVersion')]",
"feed_url": "[parameters('extensionFeed')]"
},
"dependsOn": [
"[resourceId('Microsoft.Web/Sites', parameters('siteName'))]"
]
}
]
}
],
"outputs": {
"siteId": {
"type": "string",
"value": "[resourceId(parameters('resourceGroupName'), 'Microsoft.Web/sites', parameters('siteName'))]"
}
}
}

View File

@ -0,0 +1,32 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"siteName": {
"type": "string"
},
"hostingPlanName": {
"type": "string"
},
"resourceGroupName": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "2015-08-01",
"name": "[parameters('siteName')]",
"type": "Microsoft.Web/sites",
"location": "West US 2",
"properties": {
"serverFarmId": "[resourceId(parameters('resourceGroupName'), 'Microsoft.Web/serverFarms', parameters('hostingPlanName'))]"
}
}
],
"outputs": {
"siteId": {
"type": "string",
"value": "[resourceId(parameters('resourceGroupName'), 'Microsoft.Web/sites', parameters('siteName'))]"
}
}
}

View File

@ -0,0 +1,249 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
public class TestCommand
{
private string _dotnetPath = GetDotnetPath();
private static string GetDotnetPath()
{
var current = new DirectoryInfo(Directory.GetCurrentDirectory());
while (current != null)
{
var dotnetSubdir = new DirectoryInfo(Path.Combine(current.FullName, ".test-dotnet"));
if (dotnetSubdir.Exists)
{
var dotnetName = Path.Combine(dotnetSubdir.FullName, "dotnet.exe");
if (!File.Exists(dotnetName))
{
throw new InvalidOperationException("dotnet directory was found but dotnet.exe is not in it");
}
return dotnetName;
}
current = current.Parent;
}
throw new InvalidOperationException("dotnet executable was not found");
}
private List<string> _cliGeneratedEnvironmentVariables = new List<string> { "MSBuildSDKsPath" };
protected string _command;
public Process CurrentProcess { get; private set; }
public Dictionary<string, string> Environment { get; } = new Dictionary<string, string>();
public event DataReceivedEventHandler ErrorDataReceived;
public event DataReceivedEventHandler OutputDataReceived;
public string WorkingDirectory { get; set; }
public ILogger Logger { get; set; }
public TestCommand(string command)
{
_command = command;
}
public void KillTree()
{
if (CurrentProcess == null)
{
throw new InvalidOperationException("No process is available to be killed");
}
CurrentProcess.KillTree();
}
public virtual async Task<CommandResult> ExecuteAsync(string args = "")
{
var resolvedCommand = _command;
ResolveCommand(ref resolvedCommand, ref args);
Logger.LogInformation($"Executing - {resolvedCommand} {args} - {WorkingDirectoryInfo()}");
return await ExecuteAsyncInternal(resolvedCommand, args);
}
private async Task<CommandResult> ExecuteAsyncInternal(string executable, string args)
{
var stdOut = new List<String>();
var stdErr = new List<String>();
CurrentProcess = CreateProcess(executable, args);
CurrentProcess.ErrorDataReceived += (s, e) =>
{
stdErr.Add(e.Data);
var handler = ErrorDataReceived;
if (handler != null)
{
handler(s, e);
}
};
CurrentProcess.OutputDataReceived += (s, e) =>
{
stdOut.Add(e.Data);
var handler = OutputDataReceived;
if (handler != null)
{
handler(s, e);
}
};
var completionTask = StartAndWaitForExitAsync(CurrentProcess);
CurrentProcess.BeginOutputReadLine();
CurrentProcess.BeginErrorReadLine();
await completionTask;
CurrentProcess.WaitForExit();
RemoveNullTerminator(stdOut);
RemoveNullTerminator(stdErr);
var stdOutString = String.Join(System.Environment.NewLine, stdOut);
var stdErrString = String.Join(System.Environment.NewLine, stdErr);
if (!string.IsNullOrWhiteSpace(stdOutString))
{
Logger.LogInformation("stdout: {out}", stdOutString);
}
if (!string.IsNullOrWhiteSpace(stdErrString))
{
Logger.LogInformation("stderr: {err}", stdErrString);
}
return new CommandResult(
CurrentProcess.StartInfo,
CurrentProcess.ExitCode,
stdOutString,
stdErrString);
}
private Process CreateProcess(string executable, string args)
{
var psi = new ProcessStartInfo
{
FileName = executable,
Arguments = args,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
UseShellExecute = false
};
RemoveCliGeneratedEnvironmentVariablesFrom(psi);
AddEnvironmentVariablesTo(psi);
AddWorkingDirectoryTo(psi);
var process = new Process
{
StartInfo = psi
};
process.EnableRaisingEvents = true;
return process;
}
private string WorkingDirectoryInfo()
{
if (WorkingDirectory == null)
{
return "";
}
return $" in {WorkingDirectory}";
}
private void RemoveNullTerminator(List<string> strings)
{
var count = strings.Count;
if (count < 1)
{
return;
}
if (strings[count - 1] == null)
{
strings.RemoveAt(count - 1);
}
}
private void ResolveCommand(ref string executable, ref string args)
{
if (executable == "dotnet")
{
executable = _dotnetPath;
return;
}
throw new ArgumentOutOfRangeException(nameof(executable));
}
private void RemoveCliGeneratedEnvironmentVariablesFrom(ProcessStartInfo psi)
{
foreach (var name in _cliGeneratedEnvironmentVariables)
{
psi.Environment.Remove(name);
}
}
private void AddEnvironmentVariablesTo(ProcessStartInfo psi)
{
foreach (var item in Environment)
{
psi.Environment[item.Key] = item.Value;
}
}
private void AddWorkingDirectoryTo(ProcessStartInfo psi)
{
if (!string.IsNullOrWhiteSpace(WorkingDirectory))
{
psi.WorkingDirectory = WorkingDirectory;
}
}
public static Task StartAndWaitForExitAsync(Process subject)
{
var taskCompletionSource = new TaskCompletionSource<object>();
subject.EnableRaisingEvents = true;
subject.Exited += (s, a) =>
{
taskCompletionSource.SetResult(null);
};
subject.Start();
return taskCompletionSource.Task;
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
internal class TestLogger: ILogger, IDisposable
{
private readonly ILoggerFactory _factory;
private readonly ILogger _logger;
public TestLogger(ILoggerFactory factory, ILogger logger)
{
_factory = factory;
_logger = logger;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
_logger.Log(logLevel, eventId, state, exception, formatter);
}
public bool IsEnabled(LogLevel logLevel)
{
return _logger.IsEnabled(logLevel);
}
public IDisposable BeginScope<TState>(TState state)
{
return _logger.BeginScope(state);
}
public void Dispose()
{
_factory.Dispose();
}
}
}

View File

@ -0,0 +1,86 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.Azure.Management.AppService.Fluent;
using Microsoft.Azure.Management.AppService.Fluent.Models;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.AzureAppServices.FunctionalTests
{
internal static class WebAppExtensions
{
public static HttpClient CreateClient(this IWebApp site)
{
var domain = site.GetHostNameBindings().First().Key;
return new HttpClient { BaseAddress = new Uri("http://" + domain) };
}
public static async Task UploadFilesAsync(this IWebApp site, DirectoryInfo from, string to, IPublishingProfile publishingProfile, ILogger logger)
{
foreach (var info in from.GetFileSystemInfos("*", SearchOption.AllDirectories))
{
if (info is FileInfo file)
{
var address = new Uri(
"ftp://" + publishingProfile.FtpUrl + to + file.FullName.Substring(from.FullName.Length).Replace('\\', '/'));
logger.LogInformation($"Uploading {file.FullName} to {address}");
var request = (FtpWebRequest)WebRequest.Create(address);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.KeepAlive = true;
request.UseBinary = true;
request.UsePassive = false;
request.Credentials = new NetworkCredential(publishingProfile.FtpUsername, publishingProfile.FtpPassword);
request.ConnectionGroupName = "group";
using (var fileStream = File.OpenRead(file.FullName))
{
using (var requestStream = await request.GetRequestStreamAsync())
{
await fileStream.CopyToAsync(requestStream);
}
}
await request.GetResponseAsync();
}
}
}
public static async Task BuildPublishProfileAsync(this IWebApp site, string projectDirectory)
{
var result = await site.Manager.WebApps.Inner.ListPublishingProfileXmlWithSecretsAsync(
site.ResourceGroupName,
site.Name,
new CsmPublishingProfileOptionsInner());
var targetDirectory = Path.Combine(projectDirectory, "Properties", "PublishProfiles");
Directory.CreateDirectory(targetDirectory);
var publishSettings = XDocument.Load(result);
foreach (var profile in publishSettings.Root.Elements("publishProfile"))
{
if ((string) profile.Attribute("publishMethod") == "MSDeploy")
{
new XDocument(
new XElement("Project",
new XElement("PropertyGroup",
new XElement("WebPublishMethod", "MSDeploy"),
new XElement("PublishProvider", "AzureWebSite"),
new XElement("UserName", (string)profile.Attribute("userName")),
new XElement("Password", (string)profile.Attribute("userPWD")),
new XElement("MSDeployServiceURL", (string)profile.Attribute("publishUrl")),
new XElement("DeployIisAppPath", (string)profile.Attribute("msdeploySite"))
)))
.Save(Path.Combine(targetDirectory, "Profile.pubxml"));
}
}
}
}
}