2016-03-23 16:37:59 -07:00
using System ;
2016-04-25 13:17:46 -07:00
using System.Collections.Generic ;
2016-06-21 19:18:48 -05:00
using System.Diagnostics ;
2016-03-23 16:37:59 -07:00
using System.IO ;
2016-04-25 13:17:46 -07:00
using System.Linq ;
2016-03-23 16:37:59 -07:00
using System.Net.Http ;
2016-06-21 19:18:48 -05:00
using System.Threading ;
2016-06-21 16:06:32 -05:00
using System.Threading.Tasks ;
2016-03-23 16:37:59 -07:00
using Microsoft.WindowsAzure.Storage ;
2016-07-08 16:02:03 -07:00
using Microsoft.WindowsAzure.Storage.Auth ;
2016-03-23 16:37:59 -07:00
using Microsoft.WindowsAzure.Storage.Blob ;
namespace Microsoft.DotNet.Cli.Build
{
public class AzurePublisher
{
2016-06-21 16:06:32 -05:00
public enum Product
{
SharedFramework ,
Host ,
HostFxr ,
Sdk ,
}
private const string s_dotnetBlobRootUrl = "https://dotnetcli.blob.core.windows.net/" + s_dotnetBlobContainerName ;
private const string s_dotnetBlobContainerName = "dotnet" ;
2016-03-23 16:37:59 -07:00
private string _connectionString { get ; set ; }
private CloudBlobContainer _blobContainer { get ; set ; }
public AzurePublisher ( )
{
2016-05-16 15:30:53 -07:00
_connectionString = EnvVars . EnsureVariable ( "CONNECTION_STRING" ) . Trim ( '"' ) ;
2016-03-23 16:37:59 -07:00
_blobContainer = GetDotnetBlobContainer ( _connectionString ) ;
}
2016-07-08 16:02:03 -07:00
public AzurePublisher ( string accountName , string accountKey )
{
_blobContainer = GetDotnetBlobContainer ( accountName , accountKey ) ;
}
2016-03-23 16:37:59 -07:00
private CloudBlobContainer GetDotnetBlobContainer ( string connectionString )
{
CloudStorageAccount storageAccount = CloudStorageAccount . Parse ( connectionString ) ;
2016-07-08 16:02:03 -07:00
return GetDotnetBlobContainer ( storageAccount ) ;
}
private CloudBlobContainer GetDotnetBlobContainer ( string accountName , string accountKey )
{
var storageCredentials = new StorageCredentials ( accountName , accountKey ) ;
var storageAccount = new CloudStorageAccount ( storageCredentials , true ) ;
return GetDotnetBlobContainer ( storageAccount ) ;
}
private CloudBlobContainer GetDotnetBlobContainer ( CloudStorageAccount storageAccount )
{
2016-03-23 16:37:59 -07:00
CloudBlobClient blobClient = storageAccount . CreateCloudBlobClient ( ) ;
return blobClient . GetContainerReference ( s_dotnetBlobContainerName ) ;
}
2016-06-21 16:06:32 -05:00
public string UploadFile ( string file , Product product , string version )
2016-03-23 16:37:59 -07:00
{
2016-06-22 15:54:07 -05:00
string url = CalculateRelativePathForFile ( file , product , version ) ;
2016-06-21 16:06:32 -05:00
CloudBlockBlob blob = _blobContainer . GetBlockBlobReference ( url ) ;
blob . UploadFromFileAsync ( file , FileMode . Open ) . Wait ( ) ;
SetBlobPropertiesBasedOnFileType ( blob ) ;
return url ;
2016-03-23 16:37:59 -07:00
}
2016-04-25 13:17:46 -07:00
public void PublishStringToBlob ( string blob , string content )
{
CloudBlockBlob blockBlob = _blobContainer . GetBlockBlobReference ( blob ) ;
blockBlob . UploadTextAsync ( content ) . Wait ( ) ;
2016-06-21 16:06:32 -05:00
SetBlobPropertiesBasedOnFileType ( blockBlob ) ;
2016-04-25 13:17:46 -07:00
}
public void CopyBlob ( string sourceBlob , string targetBlob )
{
CloudBlockBlob source = _blobContainer . GetBlockBlobReference ( sourceBlob ) ;
CloudBlockBlob target = _blobContainer . GetBlockBlobReference ( targetBlob ) ;
// Create the empty blob
using ( MemoryStream ms = new MemoryStream ( ) )
{
target . UploadFromStreamAsync ( ms ) . Wait ( ) ;
}
// Copy actual blob data
target . StartCopyAsync ( source ) . Wait ( ) ;
}
2016-07-15 11:40:56 -07:00
public void SetBlobPropertiesBasedOnFileType ( string path )
{
CloudBlockBlob blob = _blobContainer . GetBlockBlobReference ( path ) ;
SetBlobPropertiesBasedOnFileType ( blob ) ;
}
2016-03-23 16:37:59 -07:00
private void SetBlobPropertiesBasedOnFileType ( CloudBlockBlob blockBlob )
{
if ( Path . GetExtension ( blockBlob . Uri . AbsolutePath . ToLower ( ) ) = = ".svg" )
{
blockBlob . Properties . ContentType = "image/svg+xml" ;
blockBlob . Properties . CacheControl = "no-cache" ;
blockBlob . SetPropertiesAsync ( ) . Wait ( ) ;
}
2016-04-28 16:55:55 -07:00
else if ( Path . GetExtension ( blockBlob . Uri . AbsolutePath . ToLower ( ) ) = = ".version" )
{
blockBlob . Properties . ContentType = "text/plain" ;
2016-06-21 16:06:32 -05:00
blockBlob . Properties . CacheControl = "no-cache" ;
2016-04-28 16:55:55 -07:00
blockBlob . SetPropertiesAsync ( ) . Wait ( ) ;
}
2016-03-23 16:37:59 -07:00
}
2016-06-21 16:06:32 -05:00
public IEnumerable < string > ListBlobs ( Product product , string version )
{
string virtualDirectory = $"{product}/{version}" ;
return ListBlobs ( virtualDirectory ) ;
}
2016-04-25 13:17:46 -07:00
public IEnumerable < string > ListBlobs ( string virtualDirectory )
{
CloudBlobDirectory blobDir = _blobContainer . GetDirectoryReference ( virtualDirectory ) ;
BlobContinuationToken continuationToken = new BlobContinuationToken ( ) ;
var blobFiles = blobDir . ListBlobsSegmentedAsync ( continuationToken ) . Result ;
2016-06-21 16:06:32 -05:00
return blobFiles . Results . Select ( bf = > bf . Uri . PathAndQuery . Replace ( $"/{s_dotnetBlobContainerName}/" , string . Empty ) ) ;
2016-04-25 13:17:46 -07:00
}
2016-06-21 19:18:48 -05:00
public string AcquireLeaseOnBlob (
string blob ,
TimeSpan ? maxWaitDefault = null ,
TimeSpan ? delayDefault = null )
2016-04-25 13:17:46 -07:00
{
2016-06-21 19:18:48 -05:00
TimeSpan maxWait = maxWaitDefault ? ? TimeSpan . FromSeconds ( 120 ) ;
TimeSpan delay = delayDefault ? ? TimeSpan . FromMilliseconds ( 500 ) ;
Stopwatch stopWatch = new Stopwatch ( ) ;
stopWatch . Start ( ) ;
// This will throw an exception with HTTP code 409 when we cannot acquire the lease
// But we should block until we can get this lease, with a timeout (maxWaitSeconds)
while ( stopWatch . ElapsedMilliseconds < maxWait . TotalMilliseconds )
{
try
{
CloudBlockBlob cloudBlob = _blobContainer . GetBlockBlobReference ( blob ) ;
Task < string > task = cloudBlob . AcquireLeaseAsync ( TimeSpan . FromMinutes ( 1 ) , null ) ;
task . Wait ( ) ;
return task . Result ;
}
catch ( Exception e )
{
Console . WriteLine ( $"Retrying lease acquisition on {blob}, {e.Message}" ) ;
Thread . Sleep ( delay ) ;
}
}
throw new Exception ( $"Unable to acquire lease on {blob}" ) ;
2016-04-25 13:17:46 -07:00
}
public void ReleaseLeaseOnBlob ( string blob , string leaseId )
{
CloudBlockBlob cloudBlob = _blobContainer . GetBlockBlobReference ( blob ) ;
AccessCondition ac = new AccessCondition ( ) { LeaseId = leaseId } ;
cloudBlob . ReleaseLeaseAsync ( ac ) . Wait ( ) ;
}
public bool IsLatestSpecifiedVersion ( string version )
{
2016-06-21 16:06:32 -05:00
Task < bool > task = _blobContainer . GetBlockBlobReference ( version ) . ExistsAsync ( ) ;
2016-04-25 13:17:46 -07:00
task . Wait ( ) ;
return task . Result ;
}
public void DropLatestSpecifiedVersion ( string version )
{
CloudBlockBlob blob = _blobContainer . GetBlockBlobReference ( version ) ;
using ( MemoryStream ms = new MemoryStream ( ) )
{
blob . UploadFromStreamAsync ( ms ) . Wait ( ) ;
}
2016-04-29 14:05:41 -07:00
}
public void CreateBlobIfNotExists ( string path )
{
2016-06-21 16:06:32 -05:00
Task < bool > task = _blobContainer . GetBlockBlobReference ( path ) . ExistsAsync ( ) ;
2016-04-29 14:05:41 -07:00
task . Wait ( ) ;
if ( ! task . Result )
{
CloudBlockBlob blob = _blobContainer . GetBlockBlobReference ( path ) ;
using ( MemoryStream ms = new MemoryStream ( ) )
{
blob . UploadFromStreamAsync ( ms ) . Wait ( ) ;
}
}
2016-05-01 19:54:06 -07:00
}
public bool TryDeleteBlob ( string path )
{
try
{
DeleteBlob ( path ) ;
2016-06-21 16:06:32 -05:00
2016-05-01 19:54:06 -07:00
return true ;
}
catch ( Exception e )
{
Console . WriteLine ( $"Deleting blob {path} failed with \r\n{e.Message}" ) ;
2016-06-21 16:06:32 -05:00
2016-05-01 19:54:06 -07:00
return false ;
}
2016-04-25 13:17:46 -07:00
}
2016-06-21 16:06:32 -05:00
private void DeleteBlob ( string path )
2016-04-25 13:17:46 -07:00
{
_blobContainer . GetBlockBlobReference ( path ) . DeleteAsync ( ) . Wait ( ) ;
}
2016-06-22 15:54:07 -05:00
public static string CalculateFullUrlForFile ( string file , Product product , string version )
2016-03-23 16:37:59 -07:00
{
2016-06-22 15:54:07 -05:00
return $"{s_dotnetBlobRootUrl}/{CalculateRelativePathForFile(file, product, version)}" ;
}
private static string CalculateRelativePathForFile ( string file , Product product , string version )
{
return $"{product}/{version}/{Path.GetFileName(file)}" ;
2016-03-23 16:37:59 -07:00
}
2016-05-16 15:30:53 -07:00
public static async Task DownloadFile ( string blobFilePath , string localDownloadPath )
2016-03-23 16:37:59 -07:00
{
2016-06-21 16:06:32 -05:00
var blobUrl = $"{s_dotnetBlobRootUrl}/{blobFilePath}" ;
2016-05-16 15:30:53 -07:00
using ( var client = new HttpClient ( ) )
{
var request = new HttpRequestMessage ( HttpMethod . Get , blobUrl ) ;
var sendTask = client . SendAsync ( request , HttpCompletionOption . ResponseHeadersRead ) ;
var response = sendTask . Result . EnsureSuccessStatusCode ( ) ;
var httpStream = await response . Content . ReadAsStreamAsync ( ) ;
2016-06-21 16:06:32 -05:00
2016-05-16 15:30:53 -07:00
using ( var fileStream = File . Create ( localDownloadPath ) )
using ( var reader = new StreamReader ( httpStream ) )
{
httpStream . CopyTo ( fileStream ) ;
fileStream . Flush ( ) ;
}
}
2016-03-23 16:37:59 -07:00
}
}
}