2017-03-02 21:04:03 -08:00
// 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.
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_dotnetBlobContainerName = "dotnet" ;
2016-03-23 16:37:59 -07:00
private string _connectionString { get ; set ; }
2016-09-22 19:02:53 -07:00
private string _containerName { get ; set ; }
2016-03-23 16:37:59 -07:00
private CloudBlobContainer _blobContainer { get ; set ; }
2016-09-22 19:02:53 -07:00
public AzurePublisher ( string containerName = s_dotnetBlobContainerName )
2016-03-23 16:37:59 -07:00
{
2016-05-16 15:30:53 -07:00
_connectionString = EnvVars . EnsureVariable ( "CONNECTION_STRING" ) . Trim ( '"' ) ;
2016-09-22 19:02:53 -07:00
_containerName = containerName ;
_blobContainer = GetDotnetBlobContainer ( _connectionString , containerName ) ;
2016-03-23 16:37:59 -07:00
}
2016-09-22 19:02:53 -07:00
public AzurePublisher ( string accountName , string accountKey , string containerName = s_dotnetBlobContainerName )
2016-07-08 16:02:03 -07:00
{
2016-09-22 19:02:53 -07:00
_containerName = containerName ;
_blobContainer = GetDotnetBlobContainer ( accountName , accountKey , containerName ) ;
2016-07-08 16:02:03 -07:00
}
2016-09-22 19:02:53 -07:00
private CloudBlobContainer GetDotnetBlobContainer ( string connectionString , string containerName )
2016-03-23 16:37:59 -07:00
{
CloudStorageAccount storageAccount = CloudStorageAccount . Parse ( connectionString ) ;
2017-03-02 21:04:03 -08:00
2016-09-22 19:02:53 -07:00
return GetDotnetBlobContainer ( storageAccount , containerName ) ;
2016-07-08 16:02:03 -07:00
}
2016-09-22 19:02:53 -07:00
private CloudBlobContainer GetDotnetBlobContainer ( string accountName , string accountKey , string containerName )
2016-07-08 16:02:03 -07:00
{
var storageCredentials = new StorageCredentials ( accountName , accountKey ) ;
var storageAccount = new CloudStorageAccount ( storageCredentials , true ) ;
2016-09-22 19:02:53 -07:00
return GetDotnetBlobContainer ( storageAccount , containerName ) ;
2016-07-08 16:02:03 -07:00
}
2016-09-22 19:02:53 -07:00
private CloudBlobContainer GetDotnetBlobContainer ( CloudStorageAccount storageAccount , string containerName )
2016-07-08 16:02:03 -07:00
{
2016-03-23 16:37:59 -07:00
CloudBlobClient blobClient = storageAccount . CreateCloudBlobClient ( ) ;
2017-03-02 21:04:03 -08:00
2016-09-22 19:02:53 -07:00
return blobClient . GetContainerReference ( containerName ) ;
2016-03-23 16:37:59 -07:00
}
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 ) ;
2017-01-05 23:15:14 -06:00
blob . UploadFromFileAsync ( file ) . Wait ( ) ;
2016-06-21 16:06:32 -05:00
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-09-22 19:02:53 -07:00
return blobFiles . Results . Select ( bf = > bf . Uri . PathAndQuery . Replace ( $"/{_containerName}/" , 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
private static string CalculateRelativePathForFile ( string file , Product product , string version )
{
return $"{product}/{version}/{Path.GetFileName(file)}" ;
2016-03-23 16:37:59 -07:00
}
}
}