From 034499eda06d0775c11dd9e1d9409543e7ab5fb6 Mon Sep 17 00:00:00 2001 From: MiYanni Date: Wed, 24 Oct 2018 13:52:33 -0700 Subject: [PATCH 01/15] Updated Aks #if statement. --- .../Aks/Commands.Aks/Commands.Aks.csproj | 1 + .../Commands/StartAzureRmAksDashboard.cs | 19 ++++++++----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/ResourceManager/Aks/Commands.Aks/Commands.Aks.csproj b/src/ResourceManager/Aks/Commands.Aks/Commands.Aks.csproj index 486079ccc64a..2f9ead41b510 100644 --- a/src/ResourceManager/Aks/Commands.Aks/Commands.Aks.csproj +++ b/src/ResourceManager/Aks/Commands.Aks/Commands.Aks.csproj @@ -112,6 +112,7 @@ ResXFileCodeGenerator Resources.Designer.cs + Designer diff --git a/src/ResourceManager/Aks/Commands.Aks/Commands/StartAzureRmAksDashboard.cs b/src/ResourceManager/Aks/Commands.Aks/Commands/StartAzureRmAksDashboard.cs index 145778368664..131d2221f364 100644 --- a/src/ResourceManager/Aks/Commands.Aks/Commands/StartAzureRmAksDashboard.cs +++ b/src/ResourceManager/Aks/Commands.Aks/Commands/StartAzureRmAksDashboard.cs @@ -178,8 +178,6 @@ public override void ExecuteCmdlet() private void PopBrowser(string uri) { -#if NETSTANDARD - var browserProcess = new Process { StartInfo = new ProcessStartInfo @@ -188,24 +186,23 @@ private void PopBrowser(string uri) Arguments = uri } }; - + var verboseMessage = Resources.StartingOnDefault; +// TODO: Remove IfDef +#if NETSTANDARD if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - WriteVerbose("Starting on OSX with open"); + verboseMessage = "Starting on OSX with open"; browserProcess.StartInfo.FileName = "open"; - browserProcess.Start(); - return; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - WriteVerbose("Starting on Unix with xdg-open"); + verboseMessage = "Starting on Unix with xdg-open"; browserProcess.StartInfo.FileName = "xdg-open"; - browserProcess.Start(); - return; } #endif - WriteVerbose(Resources.StartingOnDefault); - Process.Start(uri); + + WriteVerbose(verboseMessage); + browserProcess.Start(); } } From 4d54d6f4a317d3918296979e95cc33238db5fd88 Mon Sep 17 00:00:00 2001 From: MiYanni Date: Wed, 24 Oct 2018 15:00:27 -0700 Subject: [PATCH 02/15] Updated AnalysisServices.Dataplane #if statements. --- .../Commands/StartAzureRmAksDashboard.cs | 6 +- .../Commands/AddAzureASAccount.cs | 9 ++- .../Commands/Export-AzureAsInstanceLog.cs | 66 +++++++++---------- .../Commands/Restart-AzureAsInstance.cs | 46 ++++++------- .../Models/AsAzureAuthenticationProvider.cs | 29 +++++--- .../Models/AsAzureClientSession.cs | 61 +++++++---------- 6 files changed, 100 insertions(+), 117 deletions(-) diff --git a/src/ResourceManager/Aks/Commands.Aks/Commands/StartAzureRmAksDashboard.cs b/src/ResourceManager/Aks/Commands.Aks/Commands/StartAzureRmAksDashboard.cs index 131d2221f364..83b4f62f4a3c 100644 --- a/src/ResourceManager/Aks/Commands.Aks/Commands/StartAzureRmAksDashboard.cs +++ b/src/ResourceManager/Aks/Commands.Aks/Commands/StartAzureRmAksDashboard.cs @@ -180,11 +180,7 @@ private void PopBrowser(string uri) { var browserProcess = new Process { - StartInfo = new ProcessStartInfo - { - UseShellExecute = true, - Arguments = uri - } + StartInfo = new ProcessStartInfo { Arguments = uri } }; var verboseMessage = Resources.StartingOnDefault; // TODO: Remove IfDef diff --git a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/AddAzureASAccount.cs b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/AddAzureASAccount.cs index 23e6c473007b..c93cfb69c242 100644 --- a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/AddAzureASAccount.cs +++ b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/AddAzureASAccount.cs @@ -81,7 +81,7 @@ protected override IAzureContext DefaultContext { get { - // Nothing to do with Azure Resource Managment context + // Nothing to do with Azure Resource Management context return null; } } @@ -141,8 +141,10 @@ protected override void TearDownHttpClientPipeline() public override void ExecuteCmdlet() { - AsAzureAccount azureAccount = new AsAzureAccount(); - azureAccount.Type = ServicePrincipal ? AsAzureAccount.AccountType.ServicePrincipal : AsAzureAccount.AccountType.User; + var azureAccount = new AsAzureAccount + { + Type = ServicePrincipal ? AsAzureAccount.AccountType.ServicePrincipal : AsAzureAccount.AccountType.User + }; SecureString password = null; if (Credential != null) @@ -183,6 +185,7 @@ public override void ExecuteCmdlet() { AsAzureClientSession.Instance.SetCurrentContext(azureAccount, AsEnvironment); } +// TODO: Remove IfDef #if NETSTANDARD var asAzureProfile = AsAzureClientSession.Instance.Login(currentProfile.Context, password, WriteWarning); #else diff --git a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/Export-AzureAsInstanceLog.cs b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/Export-AzureAsInstanceLog.cs index 6915dcdba5a1..cc7f83e36a33 100644 --- a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/Export-AzureAsInstanceLog.cs +++ b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/Export-AzureAsInstanceLog.cs @@ -26,7 +26,6 @@ namespace Microsoft.Azure.Commands.AnalysisServices.Dataplane { - /// /// Cmdlet to export an Analysis Services server log to file /// @@ -35,7 +34,7 @@ namespace Microsoft.Azure.Commands.AnalysisServices.Dataplane [OutputType(typeof(void))] public class ExportAzureAnalysisServerLog : AzurePSCmdlet { - private string serverName; + private string _serverName; [Parameter(Mandatory = true, HelpMessage = "Name of the Azure Analysis Services which log will be fetched")] [ValidateNotNullOrEmpty] @@ -55,21 +54,21 @@ public class ExportAzureAnalysisServerLog : AzurePSCmdlet public ExportAzureAnalysisServerLog() { - this.AsAzureHttpClient = new AsAzureHttpClient(() => new HttpClient()); - this.TokenCacheItemProvider = new TokenCacheItemProvider(); + AsAzureHttpClient = new AsAzureHttpClient(() => new HttpClient()); + TokenCacheItemProvider = new TokenCacheItemProvider(); } - public ExportAzureAnalysisServerLog(IAsAzureHttpClient AsAzureHttpClient, ITokenCacheItemProvider TokenCacheItemProvider) + public ExportAzureAnalysisServerLog(IAsAzureHttpClient asAzureHttpClient, ITokenCacheItemProvider tokenCacheItemProvider) { - this.AsAzureHttpClient = AsAzureHttpClient; - this.TokenCacheItemProvider = TokenCacheItemProvider; + AsAzureHttpClient = asAzureHttpClient; + TokenCacheItemProvider = tokenCacheItemProvider; } protected override IAzureContext DefaultContext { get { - // Nothing to do with Azure Resource Managment context + // Nothing to do with Azure Resource Management context return null; } } @@ -91,14 +90,14 @@ protected override void BeginProcessing() throw new PSInvalidOperationException(string.Format(Resources.NotLoggedInMessage, "")); } - serverName = Instance; + _serverName = Instance; Uri uriResult; - // if the user specifies the FQN of the server, then extract the servername out of that. + // if the user specifies the FQN of the server, then extract the server name out of that. // and set the current context if (Uri.TryCreate(Instance, UriKind.Absolute, out uriResult) && uriResult.Scheme == "asazure") { - serverName = uriResult.PathAndQuery.Trim('/'); + _serverName = uriResult.PathAndQuery.Trim('/'); if (string.Compare(AsAzureClientSession.Instance.Profile.Context.Environment.Name, uriResult.DnsSafeHost, StringComparison.InvariantCultureIgnoreCase) != 0) { AsAzureClientSession.Instance.SetCurrentContext( @@ -112,21 +111,18 @@ protected override void BeginProcessing() if (currentContext != null && AsAzureClientSession.AsAzureRolloutEnvironmentMapping.ContainsKey(currentContext.Environment.Name)) { - throw new PSInvalidOperationException(string.Format(Resources.InvalidServerName, serverName)); + throw new PSInvalidOperationException(string.Format(Resources.InvalidServerName, _serverName)); } } - if (this.AsAzureHttpClient == null) + if (AsAzureHttpClient == null) { - this.AsAzureHttpClient = new AsAzureHttpClient(() => - { - return new HttpClient(); - }); + AsAzureHttpClient = new AsAzureHttpClient(() => new HttpClient()); } - if (this.TokenCacheItemProvider == null) + if (TokenCacheItemProvider == null) { - this.TokenCacheItemProvider = new TokenCacheItemProvider(); + TokenCacheItemProvider = new TokenCacheItemProvider(); } } @@ -140,48 +136,46 @@ public override void ExecuteCmdlet() if (ShouldProcess(Instance, Resources.ExportingLogFromAnalysisServicesServer)) { var context = AsAzureClientSession.Instance.Profile.Context; -#if NETSTANDARD - AsAzureClientSession.Instance.Login(context, null, null); -#else AsAzureClientSession.Instance.Login(context, null); -#endif - string accessToken = this.TokenCacheItemProvider.GetTokenFromTokenCache( + + var accessToken = TokenCacheItemProvider.GetTokenFromTokenCache( AsAzureClientSession.TokenCache, context.Account.UniqueId); - Uri logfileBaseUri = - new Uri(string.Format("{0}{1}{2}", Uri.UriSchemeHttps, Uri.SchemeDelimiter, context.Environment.Name)); + var logfileBaseUri = new Uri($"{Uri.UriSchemeHttps}{Uri.SchemeDelimiter}{context.Environment.Name}"); - UriBuilder resolvedUriBuilder = new UriBuilder(logfileBaseUri); - resolvedUriBuilder.Host = ClusterResolve(logfileBaseUri, accessToken, serverName); + var resolvedUriBuilder = new UriBuilder(logfileBaseUri) + { + Host = ClusterResolve(logfileBaseUri, accessToken, _serverName) + }; var logfileEndpoint = string.Format( (string) context.Environment.Endpoints[AsAzureEnvironment.AsRolloutEndpoints.LogfileEndpointFormat], - serverName); + _serverName); - this.AsAzureHttpClient.resetHttpClient(); - using (HttpResponseMessage message = AsAzureHttpClient.CallGetAsync( + AsAzureHttpClient.resetHttpClient(); + using (var message = AsAzureHttpClient.CallGetAsync( resolvedUriBuilder.Uri, logfileEndpoint, accessToken).ConfigureAwait(false).GetAwaiter().GetResult()) { message.EnsureSuccessStatusCode(); - string actionWarning = string.Format(CultureInfo.CurrentCulture, Resources.ExportingLogOverwriteWarning, this.OutputPath); - if (AzureSession.Instance.DataStore.FileExists(this.OutputPath) && !this.Force.IsPresent && !ShouldContinue(actionWarning, Resources.Confirm)) + var actionWarning = string.Format(CultureInfo.CurrentCulture, Resources.ExportingLogOverwriteWarning, OutputPath); + if (AzureSession.Instance.DataStore.FileExists(OutputPath) && !Force.IsPresent && !ShouldContinue(actionWarning, Resources.Confirm)) { return; } - AzureSession.Instance.DataStore.WriteFile(this.OutputPath, message.Content.ReadAsStringAsync().Result); + AzureSession.Instance.DataStore.WriteFile(OutputPath, message.Content.ReadAsStringAsync().Result); } } } private string ClusterResolve(Uri clusterUri, string accessToken, string serverName) { - var resolveEndpoint = "/webapi/clusterResolve"; + const string resolveEndpoint = "/webapi/clusterResolve"; var content = new StringContent($"ServerName={serverName}"); content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded"); - using (HttpResponseMessage message = AsAzureHttpClient.CallPostAsync( + using (var message = AsAzureHttpClient.CallPostAsync( clusterUri, resolveEndpoint, accessToken, diff --git a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/Restart-AzureAsInstance.cs b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/Restart-AzureAsInstance.cs index 8a8350327c52..55a3460fe7bc 100644 --- a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/Restart-AzureAsInstance.cs +++ b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Commands/Restart-AzureAsInstance.cs @@ -30,7 +30,7 @@ namespace Microsoft.Azure.Commands.AnalysisServices.Dataplane [OutputType(typeof(bool))] public class RestartAzureAnalysisServer : AzurePSCmdlet { - private string serverName; + private string _serverName; [Parameter(Mandatory = true, HelpMessage = "Name of the Azure Analysis Services server to restart")] [ValidateNotNullOrEmpty] @@ -45,22 +45,22 @@ public class RestartAzureAnalysisServer : AzurePSCmdlet public RestartAzureAnalysisServer() { - this.AsAzureHttpClient = new AsAzureHttpClient(() => new HttpClient()); - this.TokenCacheItemProvider = new TokenCacheItemProvider(); + AsAzureHttpClient = new AsAzureHttpClient(() => new HttpClient()); + TokenCacheItemProvider = new TokenCacheItemProvider(); } - public RestartAzureAnalysisServer(IAsAzureHttpClient AsAzureHttpClient, ITokenCacheItemProvider TokenCacheItemProvider) + public RestartAzureAnalysisServer(IAsAzureHttpClient asAzureHttpClient, ITokenCacheItemProvider tokenCacheItemProvider) { - this.AsAzureHttpClient = AsAzureHttpClient; - this.TokenCacheItemProvider = TokenCacheItemProvider; + AsAzureHttpClient = asAzureHttpClient; + TokenCacheItemProvider = tokenCacheItemProvider; } protected override IAzureContext DefaultContext { get { - // Nothing to do with Azure Resource Managment context + // Nothing to do with Azure Resource Management context return null; } } @@ -82,14 +82,14 @@ protected override void BeginProcessing() throw new PSInvalidOperationException(string.Format(Resources.NotLoggedInMessage, "")); } - serverName = Instance; + _serverName = Instance; Uri uriResult; - // if the user specifies the FQN of the server, then extract the servername out of that. + // if the user specifies the FQN of the server, then extract the server name out of that. // and set the current context if (Uri.TryCreate(Instance, UriKind.Absolute, out uriResult) && uriResult.Scheme == "asazure") { - serverName = uriResult.PathAndQuery.Trim('/'); + _serverName = uriResult.PathAndQuery.Trim('/'); if (string.Compare(AsAzureClientSession.Instance.Profile.Context.Environment.Name, uriResult.DnsSafeHost, StringComparison.InvariantCultureIgnoreCase) != 0) { AsAzureClientSession.Instance.SetCurrentContext( @@ -103,21 +103,18 @@ protected override void BeginProcessing() if (currentContext != null && AsAzureClientSession.AsAzureRolloutEnvironmentMapping.ContainsKey(currentContext.Environment.Name)) { - throw new PSInvalidOperationException(string.Format(Resources.InvalidServerName, serverName)); + throw new PSInvalidOperationException(string.Format(Resources.InvalidServerName, _serverName)); } } - if (this.AsAzureHttpClient == null) + if (AsAzureHttpClient == null) { - this.AsAzureHttpClient = new AsAzureHttpClient(() => - { - return new HttpClient(); - }); + AsAzureHttpClient = new AsAzureHttpClient(() => new HttpClient()); } - if (this.TokenCacheItemProvider == null) + if (TokenCacheItemProvider == null) { - this.TokenCacheItemProvider = new TokenCacheItemProvider(); + TokenCacheItemProvider = new TokenCacheItemProvider(); } } @@ -131,18 +128,15 @@ public override void ExecuteCmdlet() if (ShouldProcess(Instance, Resources.RestartingAnalysisServicesServer)) { var context = AsAzureClientSession.Instance.Profile.Context; -#if NETSTANDARD - AsAzureClientSession.Instance.Login(context, null, null); -#else AsAzureClientSession.Instance.Login(context, null); -#endif - string accessToken = this.TokenCacheItemProvider.GetTokenFromTokenCache(AsAzureClientSession.TokenCache, context.Account.UniqueId); - Uri restartBaseUri = new Uri(string.Format("{0}{1}{2}", Uri.UriSchemeHttps, Uri.SchemeDelimiter, context.Environment.Name)); + var accessToken = TokenCacheItemProvider.GetTokenFromTokenCache(AsAzureClientSession.TokenCache, context.Account.UniqueId); + + var restartBaseUri = new Uri($"{Uri.UriSchemeHttps}{Uri.SchemeDelimiter}{context.Environment.Name}"); - var restartEndpoint = string.Format((string)context.Environment.Endpoints[AsAzureEnvironment.AsRolloutEndpoints.RestartEndpointFormat], serverName); + var restartEndpoint = string.Format((string)context.Environment.Endpoints[AsAzureEnvironment.AsRolloutEndpoints.RestartEndpointFormat], _serverName); - using (HttpResponseMessage message = AsAzureHttpClient.CallPostAsync( + using (var message = AsAzureHttpClient.CallPostAsync( restartBaseUri, restartEndpoint, accessToken).Result) diff --git a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureAuthenticationProvider.cs b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureAuthenticationProvider.cs index 009847b628a0..df18308a617b 100644 --- a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureAuthenticationProvider.cs +++ b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureAuthenticationProvider.cs @@ -18,9 +18,9 @@ using Microsoft.IdentityModel.Clients.ActiveDirectory; using Microsoft.WindowsAzure.Commands.Common.Properties; using Microsoft.Azure.Commands.Common.Authentication.Abstractions; +// TODO: Remove IfDef #if NETSTANDARD using Microsoft.WindowsAzure.Commands.Common; -using Microsoft.Rest.Azure.Authentication; #endif namespace Microsoft.Azure.Commands.AnalysisServices.Dataplane.Models @@ -37,6 +37,7 @@ public class AsAzureAuthInfo public interface IAsAzureAuthenticationProvider { +// TODO: Remove IfDef #if NETSTANDARD string GetAadAuthenticatedToken(AsAzureContext asAzureContext, SecureString password, Action promptAction, string clientId, string resourceUri, Uri resourceRedirectUri); #else @@ -46,28 +47,32 @@ public interface IAsAzureAuthenticationProvider public class AsAzureAuthenticationProvider : IAsAzureAuthenticationProvider { +// TODO: Remove IfDef #if NETSTANDARD public string GetAadAuthenticatedToken(AsAzureContext asAzureContext, SecureString password, Action promptAction, string clientId, string resourceUri, Uri resourceRedirectUri) #else public string GetAadAuthenticatedToken(AsAzureContext asAzureContext, SecureString password, PromptBehavior promptBehavior, string clientId, string resourceUri, Uri resourceRedirectUri) #endif { - var authUriBuilder = new UriBuilder((string)asAzureContext.Environment.Endpoints[AsAzureEnvironment.AsRolloutEndpoints.AdAuthorityBaseUrl]); - authUriBuilder.Path = string.IsNullOrEmpty(asAzureContext.Account.Tenant) - ? "common" - : asAzureContext.Account.Tenant; + var authUriBuilder = new UriBuilder((string)asAzureContext.Environment.Endpoints[AsAzureEnvironment.AsRolloutEndpoints.AdAuthorityBaseUrl]) + { + Path = string.IsNullOrEmpty(asAzureContext.Account.Tenant) + ? "common" + : asAzureContext.Account.Tenant + }; var authenticationContext = new AuthenticationContext( authUriBuilder.ToString(), AsAzureClientSession.TokenCache); AuthenticationResult result = null; - string accountType = string.IsNullOrEmpty(asAzureContext.Account.Type) ? AsAzureAccount.AccountType.User : asAzureContext.Account.Type; + var accountType = string.IsNullOrEmpty(asAzureContext.Account.Type) ? AsAzureAccount.AccountType.User : asAzureContext.Account.Type; if (password == null && accountType == AsAzureAccount.AccountType.User) { if (asAzureContext.Account.Id != null) { +// TODO: Remove IfDef #if NETSTANDARD result = authenticationContext.AcquireTokenAsync( resourceUri, @@ -86,6 +91,7 @@ public string GetAadAuthenticatedToken(AsAzureContext asAzureContext, SecureStri } else { +// TODO: Remove IfDef #if NETSTANDARD result = authenticationContext.AcquireTokenAsync( resourceUri, @@ -109,12 +115,13 @@ public string GetAadAuthenticatedToken(AsAzureContext asAzureContext, SecureStri { if (accountType == AsAzureAccount.AccountType.User) { +// TODO: Remove IfDef #if NETSTANDARD //https://stackoverflow.com/a/39393039/294804 //https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/issues/482 //https://github.com/Azure-Samples/active-directory-dotnet-deviceprofile/blob/5d5499d09c918ae837810d457822474df97600e9/DirSearcherClient/Program.cs#L206-L210 // Note: More robust implementation in UserTokenProvider.Netcore.cs in DoAcquireToken - DeviceCodeResult codeResult = authenticationContext.AcquireDeviceCodeAsync(resourceUri, clientId).Result; + var codeResult = authenticationContext.AcquireDeviceCodeAsync(resourceUri, clientId).Result; promptAction(codeResult?.Message); result = authenticationContext.AcquireTokenByDeviceCodeAsync(codeResult).Result; #else @@ -130,8 +137,9 @@ public string GetAadAuthenticatedToken(AsAzureContext asAzureContext, SecureStri { if (string.IsNullOrEmpty(asAzureContext.Account.CertificateThumbprint)) { +// TODO: Remove IfDef #if NETSTANDARD - ClientCredential credential = new ClientCredential(asAzureContext.Account.Id, ConversionUtilities.SecureStringToString(password)); + var credential = new ClientCredential(asAzureContext.Account.Id, ConversionUtilities.SecureStringToString(password)); result = authenticationContext.AcquireTokenAsync(resourceUri, credential).Result; #else ClientCredential credential = new ClientCredential(asAzureContext.Account.Id, password); @@ -140,14 +148,15 @@ public string GetAadAuthenticatedToken(AsAzureContext asAzureContext, SecureStri } else { - DiskDataStore dataStore = new DiskDataStore(); + var dataStore = new DiskDataStore(); var certificate = dataStore.GetCertificate(asAzureContext.Account.CertificateThumbprint); if (certificate == null) { throw new ArgumentException(string.Format(Resources.CertificateNotFoundInStore, asAzureContext.Account.CertificateThumbprint)); } +// TODO: Remove IfDef #if NETSTANDARD - result = authenticationContext.AcquireTokenAsync(resourceUri, new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate(asAzureContext.Account.Id, certificate)).Result; + result = authenticationContext.AcquireTokenAsync(resourceUri, new ClientAssertionCertificate(asAzureContext.Account.Id, certificate)).Result; #else result = authenticationContext.AcquireToken(resourceUri, new ClientAssertionCertificate(asAzureContext.Account.Id, certificate)); #endif diff --git a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureClientSession.cs b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureClientSession.cs index 7a352e3721ef..c20055dd8cc8 100644 --- a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureClientSession.cs +++ b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureClientSession.cs @@ -18,7 +18,6 @@ using System.Linq; using System.Security; using Microsoft.Azure.Commands.AnalysisServices.Dataplane.Models; -using Enumerable = System.Linq.Enumerable; namespace Microsoft.Azure.Commands.AnalysisServices.Dataplane { @@ -34,8 +33,8 @@ public class AsAzureClientSession public static readonly Uri RedirectUri = new Uri("urn:ietf:wg:oauth:2.0:oob"); public static string DefaultRolloutEnvironmentKey = "asazure.windows.net"; - public static Dictionary AsAzureRolloutEnvironmentMapping = new Dictionary() - { + public static Dictionary AsAzureRolloutEnvironmentMapping = new Dictionary + { { "asazure.windows.net", new AsAzureAuthInfo() { AuthorityUrl = "https://login.windows.net" , @@ -48,18 +47,13 @@ public class AsAzureClientSession DefaultResourceUriSuffix = "*.asazure-int.windows.net" } } - }; + }; /// /// Gets or sets the token cache store. /// public static TokenCache TokenCache { get; set; } - /// - /// As Azure Profile - /// - private AsAzureProfile _profile; - private IAsAzureAuthenticationProvider _asAzureAuthenticationProvider; static AsAzureClientSession() @@ -70,7 +64,7 @@ static AsAzureClientSession() private AsAzureClientSession() { TokenCache = new TokenCache(); - _profile = new AsAzureProfile(); + Profile = new AsAzureProfile(); _asAzureAuthenticationProvider = new AsAzureAuthenticationProvider(); } @@ -81,63 +75,61 @@ public void SetAsAzureAuthenticationProvider(IAsAzureAuthenticationProvider asAz public static AsAzureClientSession Instance { get; private set; } - public AsAzureProfile Profile - { - get { return _profile; } - set - { - _profile = value; - } - } - + /// + /// As Azure Profile + /// + public AsAzureProfile Profile { get; set; } +// TODO: Remove IfDef #if NETSTANDARD - public AsAzureProfile Login(AsAzureContext asAzureContext, SecureString password, Action promptAction) + public AsAzureProfile Login(AsAzureContext asAzureContext, SecureString password, Action promptAction = null) #else public AsAzureProfile Login(AsAzureContext asAzureContext, SecureString password) #endif { var resourceUri = new UriBuilder(Uri.UriSchemeHttps, GetResourceUriSuffix(asAzureContext.Environment.Name)).ToString(); resourceUri = resourceUri.TrimEnd('/'); +// TODO: Remove IfDef #if NETSTANDARD _asAzureAuthenticationProvider.GetAadAuthenticatedToken(asAzureContext, password, promptAction, AsAzureClientId, resourceUri, RedirectUri); #else _asAzureAuthenticationProvider.GetAadAuthenticatedToken(asAzureContext, password, password == null ? PromptBehavior.Always : PromptBehavior.Auto, AsAzureClientId, resourceUri, RedirectUri); #endif - _profile.Context.TokenCache = AsAzureClientSession.TokenCache.Serialize(); + Profile.Context.TokenCache = TokenCache.Serialize(); - if (!_profile.Environments.ContainsKey(asAzureContext.Environment.Name)) + if (!Profile.Environments.ContainsKey(asAzureContext.Environment.Name)) { - _profile.Environments.Add(asAzureContext.Environment.Name, asAzureContext.Environment); + Profile.Environments.Add(asAzureContext.Environment.Name, asAzureContext.Environment); } - return _profile; + return Profile; } public AsAzureProfile Login(AsAzureContext asAzureContext) { var resourceUri = new UriBuilder(Uri.UriSchemeHttps, GetResourceUriSuffix(asAzureContext.Environment.Name)).ToString(); resourceUri = resourceUri.TrimEnd('/'); +// TODO: Remove IfDef #if NETSTANDARD _asAzureAuthenticationProvider.GetAadAuthenticatedToken(asAzureContext, null, null, AsAzureClientId, resourceUri, RedirectUri); #else _asAzureAuthenticationProvider.GetAadAuthenticatedToken(asAzureContext, null, PromptBehavior.RefreshSession, AsAzureClientId, resourceUri, RedirectUri); #endif - _profile.Context.TokenCache = AsAzureClientSession.TokenCache.Serialize(); + Profile.Context.TokenCache = TokenCache.Serialize(); - if (!_profile.Environments.ContainsKey(asAzureContext.Environment.Name)) + if (!Profile.Environments.ContainsKey(asAzureContext.Environment.Name)) { - _profile.Environments.Add(asAzureContext.Environment.Name, asAzureContext.Environment); + Profile.Environments.Add(asAzureContext.Environment.Name, asAzureContext.Environment); } - return _profile; + return Profile; } public static string GetAuthorityUrlForEnvironment(AsAzureEnvironment environment) { var environmentKey = AsAzureRolloutEnvironmentMapping.Keys.FirstOrDefault(s => environment.Name.Contains(s)); - AsAzureAuthInfo authInfo = null; + AsAzureAuthInfo authInfo; if (string.IsNullOrEmpty(environmentKey) || !AsAzureRolloutEnvironmentMapping.TryGetValue(environmentKey, out authInfo)) { throw new ArgumentException(Properties.Resources.UnknownEnvironment); @@ -148,9 +140,9 @@ public static string GetAuthorityUrlForEnvironment(AsAzureEnvironment environmen public void SetCurrentContext(AsAzureAccount azureAccount, AsAzureEnvironment asEnvironment) { - _profile.Context = new AsAzureContext(azureAccount, asEnvironment) + Profile.Context = new AsAzureContext(azureAccount, asEnvironment) { - TokenCache = AsAzureClientSession.TokenCache.Serialize() + TokenCache = TokenCache.Serialize() }; } @@ -167,12 +159,7 @@ public static string GetResourceUriSuffix(string environmentName) } var authoInfo = AsAzureRolloutEnvironmentMapping.FirstOrDefault(kv => kv.Key.Equals(environmentName)).Value; - if (authoInfo != null) - { - return authoInfo.DefaultResourceUriSuffix; - } - - return environmentName; + return authoInfo != null ? authoInfo.DefaultResourceUriSuffix : environmentName; } } } From 1ac3790a517e74c78c56dd4dec87e377fc0e753c Mon Sep 17 00:00:00 2001 From: MiYanni Date: Wed, 24 Oct 2018 15:44:50 -0700 Subject: [PATCH 03/15] Updated Batch and ContainerInstance #if statements. --- .../Models/AsAzureClientSession.cs | 4 +- .../Common/RequestSettings.cs | 23 ++++---- .../Models/BatchClient.ApplicationPackages.cs | 23 ++++---- .../Commands/NewAzureContainerGroupCommand.cs | 56 ++++++++++--------- 4 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureClientSession.cs b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureClientSession.cs index c20055dd8cc8..fb1ea10ef4d8 100644 --- a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureClientSession.cs +++ b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices.Dataplane/Models/AsAzureClientSession.cs @@ -35,13 +35,13 @@ public class AsAzureClientSession public static Dictionary AsAzureRolloutEnvironmentMapping = new Dictionary { - { "asazure.windows.net", new AsAzureAuthInfo() + { "asazure.windows.net", new AsAzureAuthInfo { AuthorityUrl = "https://login.windows.net" , DefaultResourceUriSuffix = "*.asazure.windows.net" } }, - { "asazure-int.windows.net", new AsAzureAuthInfo() + { "asazure-int.windows.net", new AsAzureAuthInfo { AuthorityUrl = "https://login.windows-ppe.net" , DefaultResourceUriSuffix = "*.asazure-int.windows.net" diff --git a/src/ResourceManager/Automation/Commands.Automation/Common/RequestSettings.cs b/src/ResourceManager/Automation/Commands.Automation/Common/RequestSettings.cs index 3248d052332e..320b3bdc2cfd 100644 --- a/src/ResourceManager/Automation/Commands.Automation/Common/RequestSettings.cs +++ b/src/ResourceManager/Automation/Commands.Automation/Common/RequestSettings.cs @@ -15,6 +15,7 @@ using Microsoft.Azure.Commands.Automation.Common; using Microsoft.Azure.Management.Automation; using System; +// TODO: Remove IfDef code #if !NETSTANDARD using System.Diagnostics.Eventing; #endif @@ -22,17 +23,18 @@ namespace Microsoft.Azure.Commands.Automation { public class RequestSettings : IDisposable { - private readonly AutomationManagementClient client; + private readonly AutomationManagementClient _client; public RequestSettings(IAutomationManagementClient automationClient) { - client = ((AutomationManagementClient)automationClient); - client.HttpClient.DefaultRequestHeaders.Remove(Constants.ClientRequestIdHeaderName); - client.HttpClient.DefaultRequestHeaders.Add(Constants.ClientRequestIdHeaderName, Guid.NewGuid().ToString()); + _client = ((AutomationManagementClient)automationClient); + _client.HttpClient.DefaultRequestHeaders.Remove(Constants.ClientRequestIdHeaderName); + _client.HttpClient.DefaultRequestHeaders.Add(Constants.ClientRequestIdHeaderName, Guid.NewGuid().ToString()); - client.HttpClient.DefaultRequestHeaders.Remove(Constants.ActivityIdHeaderName); + _client.HttpClient.DefaultRequestHeaders.Remove(Constants.ActivityIdHeaderName); var activityId = Guid.NewGuid(); - client.HttpClient.DefaultRequestHeaders.Add(Constants.ActivityIdHeaderName, activityId.ToString()); + _client.HttpClient.DefaultRequestHeaders.Add(Constants.ActivityIdHeaderName, activityId.ToString()); +// TODO: Remove IfDef code #if !NETSTANDARD EventProvider.SetActivityId(ref activityId); #endif @@ -45,11 +47,10 @@ public void Dispose() protected virtual void Dispose(bool disposing) { - if (disposing) - { - client.HttpClient.DefaultRequestHeaders.Remove(Constants.ClientRequestIdHeaderName); - client.HttpClient.DefaultRequestHeaders.Remove(Constants.ActivityIdHeaderName); - } + if (!disposing) return; + + _client.HttpClient.DefaultRequestHeaders.Remove(Constants.ClientRequestIdHeaderName); + _client.HttpClient.DefaultRequestHeaders.Remove(Constants.ActivityIdHeaderName); } } } diff --git a/src/ResourceManager/AzureBatch/Commands.Batch/Models/BatchClient.ApplicationPackages.cs b/src/ResourceManager/AzureBatch/Commands.Batch/Models/BatchClient.ApplicationPackages.cs index 76941d5a26ee..9599f4956202 100644 --- a/src/ResourceManager/AzureBatch/Commands.Batch/Models/BatchClient.ApplicationPackages.cs +++ b/src/ResourceManager/AzureBatch/Commands.Batch/Models/BatchClient.ApplicationPackages.cs @@ -47,13 +47,13 @@ public virtual PSApplicationPackage GetApplicationPackage(string resourceGroupNa resourceGroupName = GetGroupForAccount(accountName); } - ApplicationPackage response = BatchManagementClient.ApplicationPackage.Get( + var response = BatchManagementClient.ApplicationPackage.Get( resourceGroupName, accountName, applicationId, version); - return this.ConvertGetApplicationPackageResponseToApplicationPackage(response); + return ConvertGetApplicationPackageResponseToApplicationPackage(response); } public virtual PSApplicationPackage UploadAndActivateApplicationPackage( @@ -117,8 +117,9 @@ private void UploadFileToApplicationPackage( { try { - CloudBlockBlob blob = new CloudBlockBlob(new Uri(storageUrl)); -#if NETSTANDARD + var blob = new CloudBlockBlob(new Uri(storageUrl)); +// TODO: Remove IfDef +#if NETSTANDARD Task.Run(() => blob.UploadFromFileAsync(filePath)).Wait(); #else blob.UploadFromFile(filePath, FileMode.Open); @@ -161,7 +162,7 @@ private void ActivateApplicationPackage(string resourceGroupName, string account } catch (Exception exception) { - string message = string.Format(errorMessageFormat, applicationId, version, exception.Message); + var message = string.Format(errorMessageFormat, applicationId, version, exception.Message); throw new NewApplicationPackageException(message, exception); } } @@ -171,7 +172,7 @@ private string GetStorageUrl(string resourceGroupName, string accountName, strin try { // Checks to see if the package exists - ApplicationPackage response = BatchManagementClient.ApplicationPackage.Get( + var response = BatchManagementClient.ApplicationPackage.Get( resourceGroupName, accountName, applicationId, @@ -192,7 +193,7 @@ private string GetStorageUrl(string resourceGroupName, string accountName, strin try { - ApplicationPackage addResponse = BatchManagementClient.ApplicationPackage.Create( + var addResponse = BatchManagementClient.ApplicationPackage.Create( resourceGroupName, accountName, applicationId, @@ -209,9 +210,9 @@ private string GetStorageUrl(string resourceGroupName, string accountName, strin } } - private PSApplicationPackage ConvertGetApplicationPackageResponseToApplicationPackage(ApplicationPackage response) + private static PSApplicationPackage ConvertGetApplicationPackageResponseToApplicationPackage(ApplicationPackage response) { - return new PSApplicationPackage() + return new PSApplicationPackage { Format = response.Format, StorageUrl = response.StorageUrl, @@ -219,7 +220,7 @@ private PSApplicationPackage ConvertGetApplicationPackageResponseToApplicationPa State = response.State.Value, Id = response.Id, Version = response.Version, - LastActivationTime = response.LastActivationTime, + LastActivationTime = response.LastActivationTime }; } @@ -230,7 +231,7 @@ private static IList ConvertApplicationPackagesToPsApplica Format = applicationPackage.Format, LastActivationTime = applicationPackage.LastActivationTime, State = applicationPackage.State.Value, - Version = applicationPackage.Version, + Version = applicationPackage.Version }).ToList(); } } diff --git a/src/ResourceManager/ContainerInstance/Commands.ContainerInstance/Commands/NewAzureContainerGroupCommand.cs b/src/ResourceManager/ContainerInstance/Commands.ContainerInstance/Commands/NewAzureContainerGroupCommand.cs index 33a37b5ecece..7e8c1cbbe744 100644 --- a/src/ResourceManager/ContainerInstance/Commands.ContainerInstance/Commands/NewAzureContainerGroupCommand.cs +++ b/src/ResourceManager/ContainerInstance/Commands.ContainerInstance/Commands/NewAzureContainerGroupCommand.cs @@ -39,7 +39,7 @@ public class NewAzureContainerGroupCommand : ContainerInstanceCmdletBase Position = 0, ValueFromPipelineByPropertyName = true, HelpMessage = "The resource group name.")] - [ResourceGroupCompleter()] + [ResourceGroupCompleter] [ValidateNotNullOrEmpty] public string ResourceGroupName { get; set; } @@ -149,7 +149,8 @@ public class NewAzureContainerGroupCommand : ContainerInstanceCmdletBase [ValidateNotNullOrEmpty] public int[] Port { get; set; } - #if !NETSTANDARD +// TODO: Remove IfDef code +#if !NETSTANDARD [Parameter( Mandatory = false, HelpMessage = "The command to run in the container.")] @@ -177,33 +178,34 @@ public class NewAzureContainerGroupCommand : ContainerInstanceCmdletBase public override void ExecuteCmdlet() { - if (this.ShouldProcess(this.Name, "Create Container Group")) + if (ShouldProcess(Name, "Create Container Group")) { - var creationParameter = new ContainerGroupCreationParameters() + var creationParameter = new ContainerGroupCreationParameters { - Name = this.Name, - ResourceGroupName = this.ResourceGroupName, - Location = this.Location ?? this.GetResourceGroupLocation(this.ResourceGroupName), - Tags = TagsConversionHelper.CreateTagDictionary(this.Tag, validate: true), - OsType = this.OsType ?? ContainerGroupCreationParameters.DefaultOsType, - RestartPolicy = this.RestartPolicy ?? ContainerGroupRestartPolicy.Always, - IpAddressType = this.IpAddressType, - DnsNameLabel = this.DnsNameLabel, - Ports = this.Port ?? ContainerGroupCreationParameters.DefaultPorts, - ContainerImage = this.Image, - EnvironmentVariables = this.ConvertHashtableToDictionary(this.EnvironmentVariable), - Cpu = this.Cpu ?? ContainerGroupCreationParameters.DefaultCpu, - MemoryInGb = this.MemoryInGB ?? ContainerGroupCreationParameters.DefaultMemory, - RegistryServer = this.RegistryServerDomain, - RegistryUsername = this.RegistryCredential?.UserName, - RegistryPassword = ContainerGroupCreationParameters.ConvertToString(this.RegistryCredential?.Password), - AzureFileVolumeShareName = this.AzureFileVolumeShareName, - AzureFileVolumeAccountName = this.AzureFileVolumeAccountCredential?.UserName, - AzureFileVolumeAccountKey = ContainerGroupCreationParameters.ConvertToString(this.AzureFileVolumeAccountCredential?.Password), - AzureFileVolumeMountPath = this.AzureFileVolumeMountPath + Name = Name, + ResourceGroupName = ResourceGroupName, + Location = Location ?? GetResourceGroupLocation(ResourceGroupName), + Tags = TagsConversionHelper.CreateTagDictionary(Tag, validate: true), + OsType = OsType ?? ContainerGroupCreationParameters.DefaultOsType, + RestartPolicy = RestartPolicy ?? ContainerGroupRestartPolicy.Always, + IpAddressType = IpAddressType, + DnsNameLabel = DnsNameLabel, + Ports = Port ?? ContainerGroupCreationParameters.DefaultPorts, + ContainerImage = Image, + EnvironmentVariables = ConvertHashtableToDictionary(EnvironmentVariable), + Cpu = Cpu ?? ContainerGroupCreationParameters.DefaultCpu, + MemoryInGb = MemoryInGB ?? ContainerGroupCreationParameters.DefaultMemory, + RegistryServer = RegistryServerDomain, + RegistryUsername = RegistryCredential?.UserName, + RegistryPassword = ContainerGroupCreationParameters.ConvertToString(RegistryCredential?.Password), + AzureFileVolumeShareName = AzureFileVolumeShareName, + AzureFileVolumeAccountName = AzureFileVolumeAccountCredential?.UserName, + AzureFileVolumeAccountKey = ContainerGroupCreationParameters.ConvertToString(AzureFileVolumeAccountCredential?.Password), + AzureFileVolumeMountPath = AzureFileVolumeMountPath }; -#if !NETSTANDARD +// TODO: Remove IfDef code +#if !NETSTANDARD if (!string.IsNullOrWhiteSpace(this.Command)) { Collection errors; @@ -218,9 +220,9 @@ public override void ExecuteCmdlet() creationParameter.Validate(); var psContainerGroup = PSContainerGroup.FromContainerGroup( - this.CreateContainerGroup(creationParameter)); + CreateContainerGroup(creationParameter)); - this.WriteObject(psContainerGroup); + WriteObject(psContainerGroup); } } } From 049e9a366b81e2b31d74fda24b2349a38a221207 Mon Sep 17 00:00:00 2001 From: MiYanni Date: Wed, 24 Oct 2018 16:50:43 -0700 Subject: [PATCH 04/15] Updated DataFactories and DataLakeAnalytics #if statements. --- .../DataSlices/SaveAzureDataFactoryLog.cs | 23 ++++---- .../Models/DataFactoryClient.DataSlices.cs | 25 ++++----- .../Models/DataLakeAnalyticsClient.cs | 54 +++++++++---------- 3 files changed, 47 insertions(+), 55 deletions(-) diff --git a/src/ResourceManager/DataFactories/Commands.DataFactories/DataSlices/SaveAzureDataFactoryLog.cs b/src/ResourceManager/DataFactories/Commands.DataFactories/DataSlices/SaveAzureDataFactoryLog.cs index bdd90c63f3ff..50dfcdecf555 100644 --- a/src/ResourceManager/DataFactories/Commands.DataFactories/DataSlices/SaveAzureDataFactoryLog.cs +++ b/src/ResourceManager/DataFactories/Commands.DataFactories/DataSlices/SaveAzureDataFactoryLog.cs @@ -60,12 +60,12 @@ public override void ExecuteCmdlet() ResourceGroupName = DataFactory.ResourceGroupName; } - Uri runLogUri = + var runLogUri = DataFactoryClient.GetDataSliceRunLogsSharedAccessSignature( ResourceGroupName, DataFactoryName, Id); if (DownloadLogs.IsPresent) { - string directory = string.IsNullOrWhiteSpace(Output) + var directory = string.IsNullOrWhiteSpace(Output) ? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) : Output; @@ -76,7 +76,7 @@ public override void ExecuteCmdlet() try { - DataFactoryClient.DownloadFileToBlob(new BlobDownloadParameters() + DataFactoryClient.DownloadFileToBlob(new BlobDownloadParameters { Directory = directory, SasUri = runLogUri, @@ -93,24 +93,20 @@ public override void ExecuteCmdlet() WriteObject(new PSRunLogInfo(runLogUri)); } - private bool HaveWriteAccess(string directory) + private static bool HaveWriteAccess(string directory) { - bool writeAllow = false; - bool writeDeny = false; + var writeAllow = false; + var writeDeny = false; try { +// TODO: Remove IfDef #if NETSTANDARD //https://stackoverflow.com/a/41019841/294804 var accessControlList = new DirectorySecurity(directory, AccessControlSections.All); #else - DirectorySecurity accessControlList = Directory.GetAccessControl(directory); + var accessControlList = Directory.GetAccessControl(directory); #endif - if (accessControlList == null) - { - return false; - } - - var rules = accessControlList.GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); + var rules = accessControlList?.GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier)); if (rules == null) { @@ -128,7 +124,6 @@ private bool HaveWriteAccess(string directory) { writeAllow = true; } - else if (rule.AccessControlType == AccessControlType.Deny) { writeDeny = true; diff --git a/src/ResourceManager/DataFactories/Commands.DataFactories/Models/DataFactoryClient.DataSlices.cs b/src/ResourceManager/DataFactories/Commands.DataFactories/Models/DataFactoryClient.DataSlices.cs index 7d7155abd9a3..85941b0867d0 100644 --- a/src/ResourceManager/DataFactories/Commands.DataFactories/Models/DataFactoryClient.DataSlices.cs +++ b/src/ResourceManager/DataFactories/Commands.DataFactories/Models/DataFactoryClient.DataSlices.cs @@ -29,7 +29,7 @@ public partial class DataFactoryClient { public virtual List ListDataSliceRuns(DataSliceRunFilterOptions filterOptions) { - List runs = new List(); + var runs = new List(); DataSliceRunListResponse response; if (filterOptions.NextLink.IsNextPageLink()) @@ -42,12 +42,12 @@ public virtual List ListDataSliceRuns(DataSliceRunFilterOptions filterOptions.ResourceGroupName, filterOptions.DataFactoryName, filterOptions.DatasetName, - new DataSliceRunListParameters() + new DataSliceRunListParameters { DataSliceStartTime = filterOptions.StartDateTime.ConvertToISO8601DateTimeString() }); } - filterOptions.NextLink = response != null ? response.NextLink : null; + filterOptions.NextLink = response?.NextLink; if (response != null && response.DataSliceRuns != null) { @@ -68,7 +68,7 @@ public virtual List ListDataSliceRuns(DataSliceRunFilterOptions public virtual List ListDataSlices(DataSliceFilterOptions filterOptions) { - List dataSlices = new List(); + var dataSlices = new List(); DataSliceListResponse response; if (filterOptions.NextLink.IsNextPageLink()) @@ -81,13 +81,13 @@ public virtual List ListDataSlices(DataSliceFilterOptions filterOpt filterOptions.ResourceGroupName, filterOptions.DataFactoryName, filterOptions.DatasetName, - new DataSliceListParameters() + new DataSliceListParameters { DataSliceRangeStartTime = filterOptions.DataSliceRangeStartTime.ConvertToISO8601DateTimeString(), DataSliceRangeEndTime = filterOptions.DataSliceRangeEndTime.ConvertToISO8601DateTimeString() }); } - filterOptions.NextLink = response != null ? response.NextLink : null; + filterOptions.NextLink = response?.NextLink; if (response != null && response.DataSlices != null) { @@ -119,7 +119,7 @@ public virtual void SetSliceStatus( resourceGroupName, dataFactoryName, datasetName, - new DataSliceSetStatusParameters() + new DataSliceSetStatusParameters { SliceState = sliceState, UpdateType = updateType, @@ -143,20 +143,20 @@ public virtual void DownloadFileToBlob(BlobDownloadParameters parameters) throw new ArgumentNullException(Resources.DownloadCredentialsNull); } - PSRunLogInfo rli = new PSRunLogInfo(parameters.SasUri); + var rli = new PSRunLogInfo(parameters.SasUri); StorageCredentials sc = new StorageCredentials(rli.SasToken); StorageUri suri = new StorageUri(new Uri("https://" + parameters.SasUri.Host)); CloudBlobClient sourceClient = new CloudBlobClient(suri, sc); CloudBlobContainer sascontainer = sourceClient.GetContainerReference(rli.Container); CloudBlobDirectory sourceDirectory = sascontainer.GetDirectoryReference(rli.Directory); - +// TODO: Remove IfDef #if NETSTANDARD var bloblist = sourceDirectory.ListBlobsSegmentedAsync(true, BlobListingDetails.None, null, null, null, null).Result.Results; #else var bloblist = sourceDirectory.ListBlobs(true); #endif - string downloadFolderPath = parameters.Directory.Insert(parameters.Directory.Length, @"\"); + var downloadFolderPath = parameters.Directory.Insert(parameters.Directory.Length, @"\"); foreach (var blob in bloblist) { @@ -164,7 +164,7 @@ public virtual void DownloadFileToBlob(BlobDownloadParameters parameters) int length = destBlob.Name.Split('/').Length; string blobFileName = destBlob.Name.Split('/')[length - 1]; // the folder structure of run logs changed from flat listing to nesting under time directory - string blobFolderPath = String.Empty; + var blobFolderPath = String.Empty; if (destBlob.Name.Length > blobFileName.Length) { blobFolderPath = destBlob.Name.Substring(0, destBlob.Name.Length - blobFileName.Length - 1); @@ -182,8 +182,9 @@ public virtual void DownloadFileToBlob(BlobDownloadParameters parameters) // adding _log suffix to differentiate between files and folders of the same name. Azure blob storage only knows about blob files. We could use nested folder structure // as part of the blob file name and thus it is possible to have a file and folder of the same name in the same location which is not acceptable for Windows file system string fileToDownload = destBlob.Name.Remove(0, rli.Directory.Length); +// TODO: Remove IfDef #if NETSTANDARD - destBlob.DownloadToFileAsync(downloadFolderPath + fileToDownload + "_log", FileMode.Create).RunSynchronously(); + Task.Run(() => destBlob.DownloadToFileAsync(downloadFolderPath + fileToDownload + "_log", FileMode.Create)).Wait(); #else destBlob.DownloadToFile(downloadFolderPath + fileToDownload + "_log", FileMode.Create); #endif diff --git a/src/ResourceManager/DataLakeAnalytics/Commands.DataLakeAnalytics/Models/DataLakeAnalyticsClient.cs b/src/ResourceManager/DataLakeAnalytics/Commands.DataLakeAnalytics/Models/DataLakeAnalyticsClient.cs index b53fa76daedd..a8f921cd5eb1 100644 --- a/src/ResourceManager/DataLakeAnalytics/Commands.DataLakeAnalytics/Models/DataLakeAnalyticsClient.cs +++ b/src/ResourceManager/DataLakeAnalytics/Commands.DataLakeAnalytics/Models/DataLakeAnalyticsClient.cs @@ -34,8 +34,7 @@ public class DataLakeAnalyticsClient private readonly DataLakeAnalyticsCatalogManagementClient _catalogClient; private readonly DataLakeAnalyticsJobManagementClient _jobClient; private readonly Guid _subscriptionId; - private static Queue jobIdQueue; - + private static Queue _jobIdQueue; /// /// Gets or sets the job identifier queue, which is used exclusively as a test hook. @@ -45,16 +44,8 @@ public class DataLakeAnalyticsClient /// public static Queue JobIdQueue { - get - { - if (jobIdQueue == null) - { - jobIdQueue = new Queue(); - } - - return jobIdQueue; - } - set { jobIdQueue = value; } + get { return _jobIdQueue ?? (_jobIdQueue = new Queue()); } + set { _jobIdQueue = value; } } public DataLakeAnalyticsClient(IAzureContext context) @@ -478,6 +469,7 @@ public List ListFirewallRules(string resourceGroupName, string acc public void CreateSecret(string accountName, string databaseName, string secretName, string password, string hostUri) { +// TODO: Remove IfDef #if NETSTANDARD _catalogClient.Catalog.CreateCredential(accountName, databaseName, secretName, new DataLakeAnalyticsCatalogCredentialCreateParameters @@ -498,6 +490,7 @@ public void CreateSecret(string accountName, string databaseName, public USqlSecret UpdateSecret(string accountName, string databaseName, string secretName, string password, string hostUri) { +// TODO: Remove IfDef #if NETSTANDARD _catalogClient.Catalog.UpdateCredential(accountName, databaseName, secretName, new DataLakeAnalyticsCatalogCredentialUpdateParameters @@ -520,6 +513,7 @@ public USqlSecret UpdateSecret(string accountName, string databaseName, public void DeleteSecret(string accountName, string databaseName, string secretName) { +// TODO: Remove IfDef #if NETSTANDARD if (string.IsNullOrEmpty(secretName)) { @@ -545,7 +539,7 @@ public void DeleteSecret(string accountName, string databaseName, string secretN #endif } - +// TODO: Remove IfDef #if NETSTANDARD public USqlCredential GetSecret(string accountName, string databaseName, string secretName) { @@ -947,7 +941,7 @@ private USqlDatabase GetDatabase(string accountName, string databaseName) private IList GetDatabases(string accountName) { - List toReturn = new List(); + var toReturn = new List(); var response = _catalogClient.Catalog.ListDatabases(accountName); toReturn.AddRange(response); while (!string.IsNullOrEmpty(response.NextPageLink)) @@ -968,7 +962,7 @@ private USqlAssembly GetAssembly(string accountName, string databaseName, private IList GetAssemblies(string accountName, string databaseName) { - List toReturn = new List(); + var toReturn = new List(); var response = _catalogClient.Catalog.ListAssemblies(accountName, databaseName); toReturn.AddRange(response); while (!string.IsNullOrEmpty(response.NextPageLink)) @@ -991,7 +985,7 @@ private USqlExternalDataSource GetExternalDataSource(string accountName, private IList GetExternalDataSources(string accountName, string databaseName) { - List toReturn = new List(); + var toReturn = new List(); var response = _catalogClient.Catalog.ListExternalDataSources(accountName, databaseName); toReturn.AddRange(response); while (!string.IsNullOrEmpty(response.NextPageLink)) @@ -1003,6 +997,7 @@ private IList GetExternalDataSources(string accountName, return toReturn; } +// TODO: Remove IfDef #if NETSTANDARD private USqlCredential GetCredential(string accountName, string databaseName, string credName) { @@ -1015,13 +1010,14 @@ private ObsoleteUSqlCredential GetCredential(string accountName, string database } #endif +// TODO: Remove IfDef #if NETSTANDARD private IList GetCredentials(string accountName, string databaseName) #else private IList GetCredentials(string accountName, string databaseName) #endif { - List toReturn = new List(); + var toReturn = new List(); var response = _catalogClient.Catalog.ListCredentials(accountName, databaseName); toReturn.AddRange(response); while (!string.IsNullOrEmpty(response.NextPageLink)) @@ -1030,6 +1026,7 @@ private IList GetCredentials(string accountName, string toReturn.AddRange(response); } +// TODO: Remove IfDef #if NETSTANDARD return toReturn; #else @@ -1045,7 +1042,7 @@ private USqlSchema GetSchema(string accountName, string databaseName, private IList GetSchemas(string accountName, string databaseName) { - List toReturn = new List(); + var toReturn = new List(); var response = _catalogClient.Catalog.ListSchemas(accountName, databaseName); toReturn.AddRange(response); while (!string.IsNullOrEmpty(response.NextPageLink)) @@ -1067,7 +1064,7 @@ private USqlTable GetTable(string accountName, string databaseName, string schem private IList GetTables(string accountName, string databaseName, string schemaName) { - List toReturn = new List(); + var toReturn = new List(); if (string.IsNullOrEmpty(schemaName)) { var response = _catalogClient.Catalog.ListTablesByDatabase(accountName, databaseName); @@ -1101,7 +1098,7 @@ private USqlTablePartition GetTablePartition(string accountName, string database private IList GetTablePartitions(string accountName, string databaseName, string schemaName, string tableName) { - List toReturn = new List(); + var toReturn = new List(); var response = _catalogClient.Catalog.ListTablePartitions(accountName, databaseName, schemaName, tableName); toReturn.AddRange(response); while (!string.IsNullOrEmpty(response.NextPageLink)) @@ -1124,7 +1121,7 @@ private USqlTableValuedFunction GetTableValuedFunction(string accountName, private IList GetTableValuedFunctions(string accountName, string databaseName, string schemaName) { - List toReturn = new List(); + var toReturn = new List(); if (string.IsNullOrEmpty(schemaName)) { var response = _catalogClient.Catalog.ListTableValuedFunctionsByDatabase(accountName, databaseName); @@ -1159,7 +1156,7 @@ private USqlTableStatistics GetTableStatistic(string accountName, string databas private IList GetTableStatistics(string accountName, string databaseName, string schemaName, string tableName) { - List toReturn = new List(); + var toReturn = new List(); if (string.IsNullOrEmpty(schemaName) && string.IsNullOrEmpty(tableName)) { var response = _catalogClient.Catalog.ListTableStatisticsByDatabase(accountName, databaseName); @@ -1203,7 +1200,7 @@ private USqlView GetView(string accountName, string databaseName, string schemaN private IList GetViews(string accountName, string databaseName, string schemaName) { - List toReturn = new List(); + var toReturn = new List(); if (string.IsNullOrEmpty(schemaName)) { var response = _catalogClient.Catalog.ListViewsByDatabase(accountName, databaseName); @@ -1238,7 +1235,7 @@ private USqlProcedure GetProcedure(string accountName, string databaseName, stri private IList GetProcedures(string accountName, string databaseName, string schemaName) { - List toReturn = new List(); + var toReturn = new List(); var response = _catalogClient.Catalog.ListProcedures(accountName, databaseName, schemaName); toReturn.AddRange(response); while (!string.IsNullOrEmpty(response.NextPageLink)) @@ -1260,7 +1257,7 @@ private USqlPackage GetPackage(string accountName, string databaseName, string s private IList GetPackages(string accountName, string databaseName, string schemaName) { - List toReturn = new List(); + var toReturn = new List(); var response = _catalogClient.Catalog.ListPackages(accountName, databaseName, schemaName); toReturn.AddRange(response); while (!string.IsNullOrEmpty(response.NextPageLink)) @@ -1275,7 +1272,7 @@ private IList GetPackages(string accountName, string databaseName, private IList GetTypes(string accountName, string databaseName, string schemaName) { - List toReturn = new List(); + var toReturn = new List(); var response = _catalogClient.Catalog.ListTypes(accountName, databaseName, schemaName); toReturn.AddRange(response); while (!string.IsNullOrEmpty(response.NextPageLink)) @@ -1452,14 +1449,13 @@ public List ListJobs(string accountName, string filter, int var jobList = new List(); var response = _jobClient.Job.List(accountName, parameters); - var curCount = 0; jobList.AddRange(response); - curCount = jobList.Count(); + var curCount = jobList.Count; while (!string.IsNullOrEmpty(response.NextPageLink) && curCount <= top.Value) { response = ListJobsWithNextLink(response.NextPageLink); jobList.AddRange(response); - curCount = jobList.Count(); + curCount = jobList.Count; } From f707802d46f6e8fc170653cbd22a40f91269f0e5 Mon Sep 17 00:00:00 2001 From: MiYanni Date: Fri, 26 Oct 2018 14:43:39 -0700 Subject: [PATCH 05/15] Updated KeyVault #if statements. --- .../Models/KeyVaultManagementCmdletBase.cs | 195 ++++++++---------- .../Models/ModelExtensions.cs | 93 ++++----- .../Commands.KeyVault/Models/PSKeyVault.cs | 21 +- .../Models/PSKeyVaultAccessPolicy.cs | 3 +- .../Models/VaultManagementClient.cs | 55 +++-- 5 files changed, 166 insertions(+), 201 deletions(-) diff --git a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/KeyVaultManagementCmdletBase.cs b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/KeyVaultManagementCmdletBase.cs index 1cd19ef12a2c..cd2a06175c32 100644 --- a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/KeyVaultManagementCmdletBase.cs +++ b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/KeyVaultManagementCmdletBase.cs @@ -12,6 +12,7 @@ // limitations under the License. // ---------------------------------------------------------------------------------- +// TODO: Remove IfDef #if NETSTANDARD using Microsoft.Azure.Graph.RBAC.Version1_6.ActiveDirectory; #else @@ -47,20 +48,16 @@ namespace Microsoft.Azure.Commands.KeyVault public class KeyVaultManagementCmdletBase : AzureRMCmdlet { - private PSKeyVaultModels.VaultManagementClient _keyVaultManagementClient; + private VaultManagementClient _keyVaultManagementClient; private DataServiceCredential _dataServiceCredential; - public PSKeyVaultModels.VaultManagementClient KeyVaultManagementClient + public VaultManagementClient KeyVaultManagementClient { get { - if (this._keyVaultManagementClient == null) - { - this._keyVaultManagementClient = new PSKeyVaultModels.VaultManagementClient(DefaultContext); - } - return this._keyVaultManagementClient; + return _keyVaultManagementClient ?? (_keyVaultManagementClient = new VaultManagementClient(DefaultContext)); } - set { this._keyVaultManagementClient = value; } + set { _keyVaultManagementClient = value; } } @@ -69,21 +66,21 @@ public ActiveDirectoryClient ActiveDirectoryClient { get { - if (_activeDirectoryClient == null) - { - _dataServiceCredential = new DataServiceCredential(AzureSession.Instance.AuthenticationFactory, DefaultProfile.DefaultContext, AzureEnvironment.Endpoint.Graph); + if (_activeDirectoryClient != null) return _activeDirectoryClient; + + _dataServiceCredential = new DataServiceCredential(AzureSession.Instance.AuthenticationFactory, DefaultProfile.DefaultContext, AzureEnvironment.Endpoint.Graph); +// TODO: Remove IfDef #if NETSTANDARD - _activeDirectoryClient = new ActiveDirectoryClient(DefaultProfile.DefaultContext); + _activeDirectoryClient = new ActiveDirectoryClient(DefaultProfile.DefaultContext); #else - _activeDirectoryClient = new ActiveDirectoryClient(new Uri(string.Format("{0}/{1}", - DefaultProfile.DefaultContext.Environment.GetEndpoint(AzureEnvironment.Endpoint.Graph), _dataServiceCredential.TenantId)), - () => Task.FromResult(_dataServiceCredential.GetToken())); + _activeDirectoryClient = new ActiveDirectoryClient(new Uri(string.Format("{0}/{1}", + DefaultProfile.DefaultContext.Environment.GetEndpoint(AzureEnvironment.Endpoint.Graph), _dataServiceCredential.TenantId)), + () => Task.FromResult(_dataServiceCredential.GetToken())); #endif - } - return this._activeDirectoryClient; + return _activeDirectoryClient; } - set { this._activeDirectoryClient = value; } + set { _activeDirectoryClient = value; } } private ResourceManagementClient _resourceClient; @@ -91,22 +88,15 @@ public ResourceManagementClient ResourceClient { get { - if (_resourceClient == null) - { - _resourceClient = AzureSession.Instance.ClientFactory.CreateArmClient(DefaultContext, AzureEnvironment.Endpoint.ResourceManager); - } - - return _resourceClient; + return _resourceClient ?? (_resourceClient = AzureSession.Instance.ClientFactory.CreateArmClient(DefaultContext, AzureEnvironment.Endpoint.ResourceManager)); } - set { this._resourceClient = value; } + set { _resourceClient = value; } } - protected List ListVaults(string resourceGroupName, Hashtable tag) + protected List ListVaults(string resourceGroupName, Hashtable tag) { - IResourceManagementClient armClient = this.ResourceClient; - - PSTagValuePair tagValuePair = new PSTagValuePair(); + var tagValuePair = new PSTagValuePair(); if (tag != null && tag.Count > 0) { tagValuePair = TagsConversionHelper.Create(tag); @@ -115,32 +105,32 @@ public ResourceManagementClient ResourceClient throw new ArgumentException(PSKeyVaultProperties.Resources.InvalidTagFormat); } } - var listResult = Enumerable.Empty(); + IEnumerable listResult; var resourceType = KeyVaultManagementClient.VaultsResourceType; if (resourceGroupName != null) { - listResult = this.ListByResourceGroup(resourceGroupName, + listResult = ListByResourceGroup(resourceGroupName, new Rest.Azure.OData.ODataQuery( r => r.ResourceType == resourceType)); } else { - listResult = this.ListResources( + listResult = ListResources( new Rest.Azure.OData.ODataQuery( r => r.ResourceType == resourceType)); } if (!string.IsNullOrEmpty(tagValuePair.Name)) { - listResult = listResult.Where(r => r.Tags != null && r.Tags.Keys != null && r.Tags.ConvertToDictionary().Keys.Where(k => string.Equals(k, tagValuePair.Name, StringComparison.OrdinalIgnoreCase)).Any()); + listResult = listResult.Where(r => r.Tags?.Keys != null && r.Tags.ConvertToDictionary().Keys.Any(k => string.Equals(k, tagValuePair.Name, StringComparison.OrdinalIgnoreCase))); } if (!string.IsNullOrEmpty(tagValuePair.Value)) { - listResult = listResult.Where(r => r.Tags != null && r.Tags.Values != null && r.Tags.ConvertToDictionary().Values.Where(v => string.Equals(v, tagValuePair.Value, StringComparison.OrdinalIgnoreCase)).Any()); + listResult = listResult.Where(r => r.Tags?.Values != null && r.Tags.ConvertToDictionary().Values.Any(v => string.Equals(v, tagValuePair.Value, StringComparison.OrdinalIgnoreCase))); } - List vaults = new List(); + var vaults = new List(); if (listResult != null) { vaults.AddRange(listResult); @@ -151,13 +141,9 @@ public ResourceManagementClient ResourceClient public virtual IEnumerable ListResources(Rest.Azure.OData.ODataQuery filter = null, ulong first = ulong.MaxValue, ulong skip = ulong.MinValue) { - IResourceManagementClient armClient = this.ResourceClient; + IResourceManagementClient armClient = ResourceClient; - return new GenericPageEnumerable( - delegate () - { - return armClient.Resources.List(filter); - }, armClient.Resources.ListNext, first, skip).Select(r => new PSKeyVaultIdentityItem(r)); + return new GenericPageEnumerable(() => armClient.Resources.List(filter), armClient.Resources.ListNext, first, skip).Select(r => new PSKeyVaultIdentityItem(r)); } private IEnumerable ListByResourceGroup( @@ -166,28 +152,25 @@ private IEnumerable ListByResourceGroup( ulong first = ulong.MaxValue, ulong skip = ulong.MinValue) { - IResourceManagementClient armClient = this.ResourceClient; + IResourceManagementClient armClient = ResourceClient; - return new GenericPageEnumerable( - delegate () - { - return armClient.ResourceGroups.ListResources(resourceGroupName, filter); - }, armClient.ResourceGroups.ListResourcesNext, first, skip).Select(r => new PSKeyVaultIdentityItem(r)); + return new GenericPageEnumerable(() => armClient.ResourceGroups.ListResources(resourceGroupName, filter), armClient.ResourceGroups.ListResourcesNext, first, skip).Select(r => new PSKeyVaultIdentityItem(r)); } protected string GetResourceGroupName(string vaultName) { - string rg = null; - var resourcesByName = this.ResourceClient.FilterResources(new FilterResourcesOptions() + var resourcesByName = ResourceClient.FilterResources(new FilterResourcesOptions { ResourceType = KeyVaultManagementClient.VaultsResourceType }); - if (resourcesByName != null && resourcesByName.Count > 0) + if (resourcesByName == null || resourcesByName.Count <= 0) return null; + + var vault = resourcesByName.FirstOrDefault(r => r.Name.Equals(vaultName, StringComparison.OrdinalIgnoreCase)); + string rg = null; + if (vault != null) { - var vault = resourcesByName.FirstOrDefault(r => r.Name.Equals(vaultName, StringComparison.OrdinalIgnoreCase)); - if (vault != null) - rg = new ResourceIdentifier(vault.Id).ResourceGroupName; + rg = new ResourceIdentifier(vault.Id).ResourceGroupName; } return rg; @@ -229,6 +212,7 @@ protected string GetCurrentUsersObjectId() string objectId = null; if (DefaultContext.Account.Type == AzureAccount.AccountType.User) { +// TODO: Remove IfDef #if NETSTANDARD objectId = ActiveDirectoryClient.GetObjectId(new ADObjectFilterOptions {UPN = DefaultContext.Account.Id}).ToString(); #else @@ -253,73 +237,73 @@ private void ThrowIfMultipleObjectIds(IEnumerable principal, s private string GetObjectIdByUpn(string upn) { - string objId = null; - if (!string.IsNullOrWhiteSpace(upn)) - { + if (string.IsNullOrWhiteSpace(upn)) return null; +// TODO: Remove IfDef #if NETSTANDARD - var user = ActiveDirectoryClient.FilterUsers(new ADObjectFilterOptions() { UPN = upn }).SingleOrDefault(); + var user = ActiveDirectoryClient.FilterUsers(new ADObjectFilterOptions { UPN = upn }).SingleOrDefault(); #else - var user = ActiveDirectoryClient.Users.Where(u => u.UserPrincipalName.Equals(upn, StringComparison.OrdinalIgnoreCase)) - .ExecuteAsync().ConfigureAwait(false).GetAwaiter().GetResult().CurrentPage.SingleOrDefault(); + var user = ActiveDirectoryClient.Users.Where(u => u.UserPrincipalName.Equals(upn, StringComparison.OrdinalIgnoreCase)) + .ExecuteAsync().ConfigureAwait(false).GetAwaiter().GetResult().CurrentPage.SingleOrDefault(); #endif - if (user != null) - { + string objId = null; + if (user != null) + { +// TODO: Remove IfDef #if NETSTANDARD - objId = user.Id.ToString(); + objId = user.Id.ToString(); #else - objId = user.ObjectId; + objId = user.ObjectId; #endif - } } return objId; } private string GetObjectIdBySpn(string spn) { - string objId = null; - if (!string.IsNullOrWhiteSpace(spn)) - { + if (string.IsNullOrWhiteSpace(spn)) return null; + +// TODO: Remove IfDef #if NETSTANDARD - var odataQuery = new Rest.Azure.OData.ODataQuery(s => s.ServicePrincipalNames.Contains(spn)); - var servicePrincipal = ActiveDirectoryClient.FilterServicePrincipals(odataQuery).SingleOrDefault(); - objId = servicePrincipal?.Id.ToString(); + var odataQuery = new Rest.Azure.OData.ODataQuery(s => s.ServicePrincipalNames.Contains(spn)); + var servicePrincipal = ActiveDirectoryClient.FilterServicePrincipals(odataQuery).SingleOrDefault(); + var objId = servicePrincipal?.Id.ToString(); #else - var servicePrincipal = ActiveDirectoryClient.ServicePrincipals.Where(s => - s.ServicePrincipalNames.Any(n => n.Equals(spn, StringComparison.OrdinalIgnoreCase))) - .ExecuteAsync().GetAwaiter().GetResult().CurrentPage.SingleOrDefault(); - objId = servicePrincipal?.ObjectId; + var servicePrincipal = ActiveDirectoryClient.ServicePrincipals.Where(s => + s.ServicePrincipalNames.Any(n => n.Equals(spn, StringComparison.OrdinalIgnoreCase))) + .ExecuteAsync().GetAwaiter().GetResult().CurrentPage.SingleOrDefault(); + var objId = servicePrincipal?.ObjectId; #endif - } return objId; } private string GetObjectIdByEmail(string email) { - string objId = null; // In ADFS, Graph cannot handle this particular combination of filters. - if (!DefaultProfile.DefaultContext.Environment.OnPremise && !string.IsNullOrWhiteSpace(email)) - { + if (DefaultProfile.DefaultContext.Environment.OnPremise || string.IsNullOrWhiteSpace(email)) return null; + + string objId = null; +// TODO: Remove IfDef #if NETSTANDARD - var users = ActiveDirectoryClient.FilterUsers(new ADObjectFilterOptions() { Mail = email }); - if (users != null) - { - ThrowIfMultipleObjectIds(users, email); - var user = users.FirstOrDefault(); - objId = user?.Id.ToString(); - } + var users = ActiveDirectoryClient.FilterUsers(new ADObjectFilterOptions { Mail = email }); + if (users != null) + { + ThrowIfMultipleObjectIds(users, email); + var user = users.FirstOrDefault(); + objId = user?.Id.ToString(); + } #else - var users = ActiveDirectoryClient.Users.Where(FilterByEmail(email)).ExecuteAsync().GetAwaiter().GetResult().CurrentPage; - if (users != null) - { - ThrowIfMultipleObjectIds(users, email); - var user = users.FirstOrDefault(); - objId = user?.ObjectId; - } -#endif + var users = ActiveDirectoryClient.Users.Where(FilterByEmail(email)).ExecuteAsync().GetAwaiter().GetResult().CurrentPage; + if (users != null) + { + ThrowIfMultipleObjectIds(users, email); + var user = users.FirstOrDefault(); + objId = user?.ObjectId; } +#endif return objId; } +// TODO: Remove IfDef code #if !NETSTANDARD private Expression> FilterByEmail(string email) { @@ -329,20 +313,14 @@ private Expression> FilterByEmail(string email) #endif private bool ValidateObjectId(string objId) { - bool isValid = false; - if (!string.IsNullOrWhiteSpace(objId)) - { + if (string.IsNullOrWhiteSpace(objId)) return false; +// TODO: Remove IfDef #if NETSTANDARD - var objectCollection = ActiveDirectoryClient.GetObjectsByObjectId(new List { objId }); + var objectCollection = ActiveDirectoryClient.GetObjectsByObjectId(new List { objId }); #else - var objectCollection = ActiveDirectoryClient.GetObjectsByObjectIdsAsync(new[] { objId }, new string[] { }).GetAwaiter().GetResult(); + var objectCollection = ActiveDirectoryClient.GetObjectsByObjectIdsAsync(new[] { objId }, new string[] { }).GetAwaiter().GetResult(); #endif - if (objectCollection.Any()) - { - isValid = true; - } - } - return isValid; + return objectCollection.Any(); } protected string GetObjectId(string objectId, string upn, string email, string spn) @@ -374,7 +352,7 @@ protected string GetObjectId(string objectId, string upn, string email, string s return objId; } - private bool IsValidGUid(string stringGuid) + private static bool IsValidGUid(string stringGuid) { Guid parsedGuid; return Guid.TryParse(stringGuid, out parsedGuid); @@ -394,13 +372,8 @@ protected bool IsValidObjectIdSyntax(string objectId) } // In ADFS, object IDs have no additional syntax restrictions. - if (DefaultProfile.DefaultContext.Environment.OnPremise) - { - return true; - } - // In AAD, object IDs must be parsable as Guids. - return IsValidGUid(objectId); + return DefaultProfile.DefaultContext.Environment.OnPremise || IsValidGUid(objectId); } protected readonly string[] DefaultPermissionsToKeys = diff --git a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/ModelExtensions.cs b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/ModelExtensions.cs index a316cba2c4d7..71f43b5b75cb 100644 --- a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/ModelExtensions.cs +++ b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/ModelExtensions.cs @@ -12,6 +12,7 @@ // limitations under the License. // ---------------------------------------------------------------------------------- +// TODO: Remove IfDef #if NETSTANDARD using Microsoft.Azure.Graph.RBAC.Version1_6.ActiveDirectory; #else @@ -26,34 +27,30 @@ namespace Microsoft.Azure.Commands.KeyVault { - static class ModelExtensions + internal static class ModelExtensions { - public static string ConstructAccessPoliciesTableAsTable(IEnumerable policies) { - StringBuilder sb = new StringBuilder(); - - if (policies != null && policies.Any()) + if (policies == null || !policies.Any()) return string.Empty; + + const string rowFormat = "{0, -43} {1, -43} {2, -43} {3, -43} {4, -43} {5, -43} {6, -43}\r\n"; + var sb = new StringBuilder(); + sb.AppendLine(); + sb.AppendFormat( rowFormat, "Tenant ID", "Object ID", "Application ID", "Permissions to keys", "Permissions to secrets", "Permissions to certificates", "Permissions to (Key Vault Managed) storage" ); + sb.AppendFormat(rowFormat, + GeneralUtilities.GenerateSeparator("Tenant ID".Length, "="), + GeneralUtilities.GenerateSeparator("Object ID".Length, "="), + GeneralUtilities.GenerateSeparator("Application ID".Length, "="), + GeneralUtilities.GenerateSeparator("Permissions To Keys".Length, "="), + GeneralUtilities.GenerateSeparator("Permissions To Secrets".Length, "="), + GeneralUtilities.GenerateSeparator("Permissions To Certificates".Length, "="), + GeneralUtilities.GenerateSeparator("Permissions To (Key Vault Managed) Storage".Length, "=")); + + foreach (var policy in policies) { - string rowFormat = "{0, -43} {1, -43} {2, -43} {3, -43} {4, -43} {5, -43} {6, -43}\r\n"; - sb.AppendLine(); - sb.AppendFormat( rowFormat, "Tenant ID", "Object ID", "Application ID", "Permissions to keys", "Permissions to secrets", "Permissions to certificates", "Permissions to (Key Vault Managed) storage" ); - sb.AppendFormat(rowFormat, - GeneralUtilities.GenerateSeparator("Tenant ID".Length, "="), - GeneralUtilities.GenerateSeparator("Object ID".Length, "="), - GeneralUtilities.GenerateSeparator("Application ID".Length, "="), - GeneralUtilities.GenerateSeparator("Permissions To Keys".Length, "="), - GeneralUtilities.GenerateSeparator("Permissions To Secrets".Length, "="), - GeneralUtilities.GenerateSeparator("Permissions To Certificates".Length, "="), - GeneralUtilities.GenerateSeparator("Permissions To (Key Vault Managed) Storage".Length, "=")); - - foreach (var policy in policies) - { - sb.AppendFormat(rowFormat, policy.TenantId.ToString(), policy.DisplayName, policy.ApplicationIdDisplayName, - TrimWithEllipsis(policy.PermissionsToKeysStr, 40), TrimWithEllipsis(policy.PermissionsToSecretsStr, 40), - TrimWithEllipsis( policy.PermissionsToCertificatesStr, 40 ), TrimWithEllipsis( policy.PermissionsToStorageStr, 40 ) ); - } - + sb.AppendFormat(rowFormat, policy.TenantId.ToString(), policy.DisplayName, policy.ApplicationIdDisplayName, + TrimWithEllipsis(policy.PermissionsToKeysStr, 40), TrimWithEllipsis(policy.PermissionsToSecretsStr, 40), + TrimWithEllipsis( policy.PermissionsToCertificatesStr, 40 ), TrimWithEllipsis( policy.PermissionsToStorageStr, 40 ) ); } return sb.ToString(); @@ -61,41 +58,37 @@ public static string ConstructAccessPoliciesTableAsTable(IEnumerable policies) { - StringBuilder sb = new StringBuilder(); + if (policies == null || !policies.Any()) return string.Empty; - if (policies != null && policies.Any()) + var sb = new StringBuilder(); + sb.AppendLine(); + foreach(var policy in policies) { + sb.AppendFormat( "{0, -43}: {1}\r\n", "Tenant ID", policy.TenantName ); + sb.AppendFormat( "{0, -43}: {1}\r\n", "Object ID", policy.ObjectId ); + sb.AppendFormat( "{0, -43}: {1}\r\n", "Application ID", policy.ApplicationIdDisplayName ); + sb.AppendFormat( "{0, -43}: {1}\r\n", "Display Name", policy.DisplayName ); + sb.AppendFormat( "{0, -43}: {1}\r\n", "Permissions to Keys", policy.PermissionsToKeysStr ); + sb.AppendFormat( "{0, -43}: {1}\r\n", "Permissions to Secrets", policy.PermissionsToSecretsStr ); + sb.AppendFormat( "{0, -43}: {1}\r\n", "Permissions to Certificates", policy.PermissionsToCertificatesStr ); + sb.AppendFormat( "{0, -43}: {1}\r\n", "Permissions to (Key Vault Managed) Storage", policy.PermissionsToStorageStr ); sb.AppendLine(); - foreach(var policy in policies) - { - sb.AppendFormat( "{0, -43}: {1}\r\n", "Tenant ID", policy.TenantName ); - sb.AppendFormat( "{0, -43}: {1}\r\n", "Object ID", policy.ObjectId ); - sb.AppendFormat( "{0, -43}: {1}\r\n", "Application ID", policy.ApplicationIdDisplayName ); - sb.AppendFormat( "{0, -43}: {1}\r\n", "Display Name", policy.DisplayName ); - sb.AppendFormat( "{0, -43}: {1}\r\n", "Permissions to Keys", policy.PermissionsToKeysStr ); - sb.AppendFormat( "{0, -43}: {1}\r\n", "Permissions to Secrets", policy.PermissionsToSecretsStr ); - sb.AppendFormat( "{0, -43}: {1}\r\n", "Permissions to Certificates", policy.PermissionsToCertificatesStr ); - sb.AppendFormat( "{0, -43}: {1}\r\n", "Permissions to (Key Vault Managed) Storage", policy.PermissionsToStorageStr ); - sb.AppendLine(); - } } return sb.ToString(); } public static string ConstructNetworkRuleSet(PSModels.PSKeyVaultNetworkRuleSet ruleSet) { - StringBuilder sb = new StringBuilder(); + if (ruleSet == null) return string.Empty; - if (ruleSet != null) - { - sb.AppendLine(); + var sb = new StringBuilder(); + sb.AppendLine(); - sb.AppendFormat("{0, -43}: {1}\r\n", "Default Action", ruleSet.DefaultAction); - sb.AppendFormat("{0, -43}: {1}\r\n", "Bypass", ruleSet.Bypass); + sb.AppendFormat("{0, -43}: {1}\r\n", "Default Action", ruleSet.DefaultAction); + sb.AppendFormat("{0, -43}: {1}\r\n", "Bypass", ruleSet.Bypass); - sb.AppendFormat("{0, -43}: {1}\r\n", "IP Rules", ruleSet.IpAddressRangesText); - sb.AppendFormat("{0, -43}: {1}\r\n", "Virtual Network Rules", ruleSet.VirtualNetworkResourceIdsText); - } + sb.AppendFormat("{0, -43}: {1}\r\n", "IP Rules", ruleSet.IpAddressRangesText); + sb.AppendFormat("{0, -43}: {1}\r\n", "Virtual Network Rules", ruleSet.VirtualNetworkResourceIdsText); return sb.ToString(); } @@ -112,14 +105,15 @@ private static string TrimWithEllipsis(string str, int maxLen) public static string GetDisplayNameForADObject(string objectId, ActiveDirectoryClient adClient) { - string displayName = ""; - string upnOrSpn = ""; + var displayName = ""; + var upnOrSpn = ""; if (adClient == null || string.IsNullOrWhiteSpace(objectId)) return displayName; try { +// TODO: Remove IfDef #if NETSTANDARD var obj = adClient.GetObjectsByObjectId(new List { objectId }).FirstOrDefault(); if (obj != null) @@ -166,7 +160,6 @@ public static string GetDisplayNameForADObject(string objectId, ActiveDirectoryC upnOrSpn = group.MailNickname; } } - #endif } catch diff --git a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/PSKeyVault.cs b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/PSKeyVault.cs index 83d32a63ac78..f9993da510c9 100644 --- a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/PSKeyVault.cs +++ b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/PSKeyVault.cs @@ -12,6 +12,7 @@ // limitations under the License. // ---------------------------------------------------------------------------------- +// TODO: Remove IfDef #if NETSTANDARD using Microsoft.Azure.Graph.RBAC.Version1_6.ActiveDirectory; #else @@ -24,7 +25,6 @@ using System.Collections.Generic; using System.Linq; - namespace Microsoft.Azure.Commands.KeyVault.Models { public class PSKeyVault : PSKeyVaultIdentityItem @@ -45,7 +45,7 @@ public PSKeyVault(Vault vault, ActiveDirectoryClient adClient) TenantId = vault.Properties.TenantId; TenantName = vaultTenantDisplayName; VaultUri = vault.Properties.VaultUri; - EnabledForDeployment = vault.Properties.EnabledForDeployment.HasValue ? vault.Properties.EnabledForDeployment.Value : false; + EnabledForDeployment = vault.Properties.EnabledForDeployment ?? false; EnabledForTemplateDeployment = vault.Properties.EnabledForTemplateDeployment; EnabledForDiskEncryption = vault.Properties.EnabledForDiskEncryption; EnableSoftDelete = vault.Properties.EnableSoftDelete; @@ -84,24 +84,25 @@ public PSKeyVault(Vault vault, ActiveDirectoryClient adClient) //allow easy updates public Vault OriginalVault { get; private set; } - static private PSKeyVaultNetworkRuleSet InitNetworkRuleSet(VaultProperties properties) + private static PSKeyVaultNetworkRuleSet InitNetworkRuleSet(VaultProperties properties) { // The service will return NULL when NetworkAcls is never set before or set with default property values // The default constructor will set default property values in SDK's NetworkRuleSet class - if (properties == null - || properties.NetworkAcls == null) + if (properties?.NetworkAcls == null) + { return new PSKeyVaultNetworkRuleSet(); + } - NetworkRuleSet networkAcls = properties.NetworkAcls; + var networkAcls = properties.NetworkAcls; PSKeyVaultNetworkRuleDefaultActionEnum defaultAct; - if (!Enum.TryParse(networkAcls.DefaultAction, true, out defaultAct)) + if (!Enum.TryParse(networkAcls.DefaultAction, true, out defaultAct)) { defaultAct = PSKeyVaultNetworkRuleDefaultActionEnum.Allow; } PSKeyVaultNetworkRuleBypassEnum bypass; - if (!Enum.TryParse(networkAcls.Bypass, true, out bypass)) + if (!Enum.TryParse(networkAcls.Bypass, true, out bypass)) { bypass = PSKeyVaultNetworkRuleBypassEnum.AzureServices; } @@ -109,13 +110,13 @@ static private PSKeyVaultNetworkRuleSet InitNetworkRuleSet(VaultProperties prope IList allowedIpAddresses = null; if (networkAcls.IpRules != null && networkAcls.IpRules.Count > 0) { - allowedIpAddresses = networkAcls.IpRules.Select(item => { return item.Value; }).ToList(); + allowedIpAddresses = networkAcls.IpRules.Select(item => item.Value).ToList(); } IList allowedVirtualNetworkResourceIds = null; if (networkAcls.VirtualNetworkRules != null && networkAcls.VirtualNetworkRules.Count > 0) { - allowedVirtualNetworkResourceIds = networkAcls.VirtualNetworkRules.Select(item => { return item.Id; }).ToList(); + allowedVirtualNetworkResourceIds = networkAcls.VirtualNetworkRules.Select(item => item.Id).ToList(); } return new PSKeyVaultNetworkRuleSet(defaultAct, bypass, allowedIpAddresses, allowedVirtualNetworkResourceIds); diff --git a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/PSKeyVaultAccessPolicy.cs b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/PSKeyVaultAccessPolicy.cs index d0c737e1fdcf..71fdbcb89db2 100644 --- a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/PSKeyVaultAccessPolicy.cs +++ b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/PSKeyVaultAccessPolicy.cs @@ -12,6 +12,7 @@ // limitations under the License. // ---------------------------------------------------------------------------------- +// TODO: Remove IfDef #if NETSTANDARD using Microsoft.Azure.Graph.RBAC.Version1_6.ActiveDirectory; #else @@ -58,7 +59,7 @@ public PSKeyVaultAccessPolicy(KeyVaultManagement.Models.AccessPolicyEntry s, Act public Guid? ApplicationId { get; private set; } public string DisplayName { get; private set; } - public string ApplicationIdDisplayName { get { return this.ApplicationId.HasValue ? this.ApplicationId.Value.ToString() : string.Empty; } } + public string ApplicationIdDisplayName { get { return ApplicationId.HasValue ? ApplicationId.Value.ToString() : string.Empty; } } public List PermissionsToKeys { get; private set; } diff --git a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/VaultManagementClient.cs b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/VaultManagementClient.cs index a508976196e3..a8cd41d4f9a7 100644 --- a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/VaultManagementClient.cs +++ b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/VaultManagementClient.cs @@ -12,6 +12,7 @@ // limitations under the License. // ---------------------------------------------------------------------------------- +// TODO: Remove IfDef #if NETSTANDARD using Microsoft.Azure.Graph.RBAC.Version1_6.ActiveDirectory; #else @@ -73,7 +74,7 @@ public PSKeyVault CreateNewVault(VaultCreationParameters parameters, ActiveDirec { if (string.IsNullOrWhiteSpace(parameters.SkuFamilyName)) throw new ArgumentNullException("parameters.SkuFamilyName"); - if (parameters.TenantId == null || parameters.TenantId == Guid.Empty) + if (parameters.TenantId == Guid.Empty) throw new ArgumentException("parameters.TenantId"); properties.Sku = new Sku @@ -94,11 +95,11 @@ public PSKeyVault CreateNewVault(VaultCreationParameters parameters, ActiveDirec { properties.CreateMode = CreateMode.Recover; } - var response = this.KeyVaultManagementClient.Vaults.CreateOrUpdate( + var response = KeyVaultManagementClient.Vaults.CreateOrUpdate( resourceGroupName: parameters.ResourceGroupName, vaultName: parameters.VaultName, - parameters: new VaultCreateOrUpdateParameters() + parameters: new VaultCreateOrUpdateParameters { Location = parameters.Location, Tags = TagsConversionHelper.CreateTagDictionary(parameters.Tags, validate: true), @@ -124,16 +125,17 @@ public PSKeyVault GetVault(string vaultName, string resourceGroupName, ActiveDir try { - var response = this.KeyVaultManagementClient.Vaults.Get(resourceGroupName, vaultName); + var response = KeyVaultManagementClient.Vaults.Get(resourceGroupName, vaultName); return new PSKeyVault(response, adClient); } catch (CloudException ce) { if (ce.Response.StatusCode == HttpStatusCode.NotFound) + { return null; - else - throw; + } + throw; } } @@ -166,7 +168,7 @@ public PSKeyVault UpdateVault( //Update the vault properties in the object received from server //Only access policies and EnabledForDeployment can be changed - VaultProperties properties = existingVault.OriginalVault.Properties; + var properties = existingVault.OriginalVault.Properties; properties.EnabledForDeployment = updatedEnabledForDeployment; properties.EnabledForTemplateDeployment = updatedEnabledForTemplateDeployment; properties.EnabledForDiskEncryption = updatedEnabledForDiskEncryption; @@ -185,8 +187,8 @@ public PSKeyVault UpdateVault( properties.AccessPolicies = (updatedPolicies == null) ? new List() : - updatedPolicies.Select(a => new AccessPolicyEntry() - { + updatedPolicies.Select(a => new AccessPolicyEntry + { TenantId = a.TenantId, ObjectId = a.ObjectId, ApplicationId = a.ApplicationId, @@ -201,10 +203,10 @@ public PSKeyVault UpdateVault( UpdateVaultNetworkRuleSetProperties(properties, updatedNetworkAcls); - var response = this.KeyVaultManagementClient.Vaults.CreateOrUpdate( + var response = KeyVaultManagementClient.Vaults.CreateOrUpdate( resourceGroupName: existingVault.ResourceGroupName, vaultName: existingVault.VaultName, - parameters: new VaultCreateOrUpdateParameters() + parameters: new VaultCreateOrUpdateParameters { Location = existingVault.Location, Properties = properties, @@ -228,7 +230,7 @@ public void DeleteVault(string vaultName, string resourceGroupName) try { - this.KeyVaultManagementClient.Vaults.Delete(resourceGroupName, vaultName); + KeyVaultManagementClient.Vaults.Delete(resourceGroupName, vaultName); } catch (CloudException ce) { @@ -252,7 +254,7 @@ public void PurgeVault(string vaultName, string location) try { - this.KeyVaultManagementClient.Vaults.PurgeDeleted(vaultName, location); + KeyVaultManagementClient.Vaults.PurgeDeleted(vaultName, location); } catch (CloudException ce) { @@ -277,16 +279,17 @@ public PSDeletedKeyVault GetDeletedVault(string vaultName, string location) try { - var response = this.KeyVaultManagementClient.Vaults.GetDeleted(vaultName, location); + var response = KeyVaultManagementClient.Vaults.GetDeleted(vaultName, location); return new PSDeletedKeyVault(response); } catch (CloudException ce) { if (ce.Response.StatusCode == HttpStatusCode.NotFound) + { return null; - else - throw; + } + throw; } } @@ -296,9 +299,9 @@ public PSDeletedKeyVault GetDeletedVault(string vaultName, string location) /// the retrieved deleted vault public List ListDeletedVaults() { - List deletedVaults = new List(); + var deletedVaults = new List(); - var response = this.KeyVaultManagementClient.Vaults.ListDeleted(); + var response = KeyVaultManagementClient.Vaults.ListDeleted(); foreach (var deletedVault in response) { @@ -307,7 +310,7 @@ public List ListDeletedVaults() while (response?.NextPageLink != null) { - response = this.KeyVaultManagementClient.Vaults.ListDeletedNext(response.NextPageLink); + response = KeyVaultManagementClient.Vaults.ListDeletedNext(response.NextPageLink); foreach (var deletedVault in response) { @@ -327,12 +330,12 @@ public List ListDeletedVaults() /// /// Vault property /// Network rule set input - static private void UpdateVaultNetworkRuleSetProperties(VaultProperties vaultProperties, PSKeyVaultNetworkRuleSet psRuleSet) + private static void UpdateVaultNetworkRuleSetProperties(VaultProperties vaultProperties, PSKeyVaultNetworkRuleSet psRuleSet) { if (vaultProperties == null) return; - NetworkRuleSet updatedRuleSet = new NetworkRuleSet(); // It contains default settings + var updatedRuleSet = new NetworkRuleSet(); // It contains default settings if (psRuleSet != null) { updatedRuleSet.DefaultAction = psRuleSet.DefaultAction.ToString(); @@ -340,10 +343,7 @@ static private void UpdateVaultNetworkRuleSetProperties(VaultProperties vaultPro if (psRuleSet.IpAddressRanges != null && psRuleSet.IpAddressRanges.Count > 0) { - updatedRuleSet.IpRules = psRuleSet.IpAddressRanges.Select(ipAddress => - { - return new IPRule() { Value = ipAddress }; - }).ToList(); + updatedRuleSet.IpRules = psRuleSet.IpAddressRanges.Select(ipAddress => new IPRule { Value = ipAddress }).ToList(); } else { // Send empty array [] to server to override default @@ -352,10 +352,7 @@ static private void UpdateVaultNetworkRuleSetProperties(VaultProperties vaultPro if (psRuleSet.VirtualNetworkResourceIds != null && psRuleSet.VirtualNetworkResourceIds.Count > 0) { - updatedRuleSet.VirtualNetworkRules = psRuleSet.VirtualNetworkResourceIds.Select(resourceId => - { - return new VirtualNetworkRule() { Id = resourceId }; - }).ToList(); + updatedRuleSet.VirtualNetworkRules = psRuleSet.VirtualNetworkResourceIds.Select(resourceId => new VirtualNetworkRule { Id = resourceId }).ToList(); } else { // Send empty array [] to server to override default From 0c0196ee0b4f091dfb0a0e2e2562909d4281e13d Mon Sep 17 00:00:00 2001 From: MiYanni Date: Fri, 26 Oct 2018 18:04:51 -0700 Subject: [PATCH 06/15] Updated Network, Profile, and RecoveryServices #if statements. --- .../NewAzureVirtualNetworkCommand.cs | 42 +-- .../Account/ConnectAzureRmAccount.cs | 43 +-- .../Utilities/CertUtils.cs | 23 +- ...zureRMRecoveryServicesVaultSettingsFile.cs | 250 +++++++++--------- 4 files changed, 180 insertions(+), 178 deletions(-) diff --git a/src/ResourceManager/Network/Commands.Network/VirtualNetwork/NewAzureVirtualNetworkCommand.cs b/src/ResourceManager/Network/Commands.Network/VirtualNetwork/NewAzureVirtualNetworkCommand.cs index 7b25eabd656c..22f48f7bbd1d 100644 --- a/src/ResourceManager/Network/Commands.Network/VirtualNetwork/NewAzureVirtualNetworkCommand.cs +++ b/src/ResourceManager/Network/Commands.Network/VirtualNetwork/NewAzureVirtualNetworkCommand.cs @@ -25,7 +25,7 @@ namespace Microsoft.Azure.Commands.Network { - [CmdletOutputBreakingChange(typeof(PSVirtualNetwork), DeprecatedOutputProperties = new string[] { "EnableVmProtection" })] + [CmdletOutputBreakingChange(typeof(PSVirtualNetwork), DeprecatedOutputProperties = new[] { "EnableVmProtection" })] [Cmdlet("New", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "VirtualNetwork", SupportsShouldProcess = true),OutputType(typeof(PSVirtualNetwork))] public class NewAzureVirtualNetworkCommand : VirtualNetworkBaseCmdlet { @@ -89,6 +89,7 @@ public class NewAzureVirtualNetworkCommand : VirtualNetworkBaseCmdlet HelpMessage = "Reference to the DDoS protection plan resource associated with the virtual network.")] public string DdosProtectionPlanId { get; set; } +// TODO: Remove IfDef code #if !NETSTANDARD [CmdletParameterBreakingChange("EnableVmProtection", ChangeDescription = "The EnableVMProtection setting is no longer supported. Setting this parameter has no impact. This parameter will be removed in a future release. Please remove it from your scripts")] [Parameter( @@ -100,7 +101,7 @@ public class NewAzureVirtualNetworkCommand : VirtualNetworkBaseCmdlet [Parameter( Mandatory = false, - HelpMessage = "Do not ask for confirmation if you want to overrite a resource")] + HelpMessage = "Do not ask for confirmation if you want to override a resource")] public SwitchParameter Force { get; set; } [Parameter(Mandatory = false, HelpMessage = "Run cmdlet in the background")] @@ -109,7 +110,7 @@ public class NewAzureVirtualNetworkCommand : VirtualNetworkBaseCmdlet public override void Execute() { base.Execute(); - var present = this.IsVirtualNetworkPresent(this.ResourceGroupName, this.Name); + var present = IsVirtualNetworkPresent(ResourceGroupName, Name); ConfirmAction( Force.IsPresent, string.Format(Properties.Resources.OverwritingResource, Name), @@ -125,36 +126,35 @@ public override void Execute() private PSVirtualNetwork CreateVirtualNetwork() { - var vnet = new PSVirtualNetwork(); - vnet.Name = this.Name; - vnet.ResourceGroupName = this.ResourceGroupName; - vnet.Location = this.Location; - vnet.AddressSpace = new PSAddressSpace(); - vnet.AddressSpace.AddressPrefixes = this.AddressPrefix; - - if (this.DnsServer != null) + var vnet = new PSVirtualNetwork { - vnet.DhcpOptions = new PSDhcpOptions(); - vnet.DhcpOptions.DnsServers = this.DnsServer; + Name = Name, + ResourceGroupName = ResourceGroupName, + Location = Location, + AddressSpace = new PSAddressSpace {AddressPrefixes = AddressPrefix} + }; + + if (DnsServer != null) + { + vnet.DhcpOptions = new PSDhcpOptions {DnsServers = DnsServer}; } - vnet.Subnets = this.Subnet; - vnet.EnableDdosProtection = this.EnableDdosProtection; + vnet.Subnets = Subnet; + vnet.EnableDdosProtection = EnableDdosProtection; - if (!string.IsNullOrEmpty(this.DdosProtectionPlanId)) + if (!string.IsNullOrEmpty(DdosProtectionPlanId)) { - vnet.DdosProtectionPlan = new PSResourceId(); - vnet.DdosProtectionPlan.Id = this.DdosProtectionPlanId; + vnet.DdosProtectionPlan = new PSResourceId {Id = DdosProtectionPlanId}; } // Map to the sdk object var vnetModel = NetworkResourceManagerProfile.Mapper.Map(vnet); - vnetModel.Tags = TagsConversionHelper.CreateTagDictionary(this.Tag, validate: true); + vnetModel.Tags = TagsConversionHelper.CreateTagDictionary(Tag, validate: true); // Execute the Create VirtualNetwork call - this.VirtualNetworkClient.CreateOrUpdate(this.ResourceGroupName, this.Name, vnetModel); + VirtualNetworkClient.CreateOrUpdate(ResourceGroupName, Name, vnetModel); - var getVirtualNetwork = this.GetVirtualNetwork(this.ResourceGroupName, this.Name); + var getVirtualNetwork = GetVirtualNetwork(ResourceGroupName, Name); return getVirtualNetwork; } diff --git a/src/ResourceManager/Profile/Commands.Profile/Account/ConnectAzureRmAccount.cs b/src/ResourceManager/Profile/Commands.Profile/Account/ConnectAzureRmAccount.cs index bc86a89abffe..ab225cb8622b 100644 --- a/src/ResourceManager/Profile/Commands.Profile/Account/ConnectAzureRmAccount.cs +++ b/src/ResourceManager/Profile/Commands.Profile/Account/ConnectAzureRmAccount.cs @@ -31,8 +31,8 @@ namespace Microsoft.Azure.Commands.Profile /// /// Cmdlet to log into an environment and download the subscriptions /// - [Cmdlet("Connect", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "Account", DefaultParameterSetName = "UserWithSubscriptionId", SupportsShouldProcess=true)] - [Alias("Login-AzAccount", "Login-AzureRmAccount", "Add-" + ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "Account")] + [Cmdlet("Connect", AzureRMConstants.AzureRMPrefix + "Account", DefaultParameterSetName = "UserWithSubscriptionId", SupportsShouldProcess=true)] + [Alias("Login-AzAccount", "Login-AzureRmAccount", "Add-" + AzureRMConstants.AzureRMPrefix + "Account")] [OutputType(typeof(PSAzureProfile))] public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IModuleAssemblyInitializer { @@ -44,13 +44,14 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod public const string MSIEndpointVariable = "MSI_ENDPOINT"; public const string MSISecretVariable = "MSI_SECRET"; - protected IAzureEnvironment _environment =AzureEnvironment.PublicEnvironments[EnvironmentName.AzureCloud]; + private IAzureEnvironment _environment = AzureEnvironment.PublicEnvironments[EnvironmentName.AzureCloud]; [Parameter(Mandatory = false, HelpMessage = "Name of the environment containing the account to log into")] [Alias("EnvironmentName")] [ValidateNotNullOrEmpty] public string Environment { get; set; } +// TODO: Remove IfDef code #if !NETSTANDARD [Parameter(ParameterSetName = UserParameterSet, Mandatory = false, HelpMessage = "Optional credential", Position = 0)] @@ -194,7 +195,7 @@ public override void ExecuteCmdlet() } - AzureAccount azureAccount = new AzureAccount(); + var azureAccount = new AzureAccount(); switch (ParameterSetName) { @@ -211,17 +212,19 @@ public override void ExecuteCmdlet() break; case ManagedServiceParameterSet: azureAccount.Type = AzureAccount.AccountType.ManagedService; - var builder = new UriBuilder(); - builder.Scheme = "http"; - builder.Host = ManagedServiceHostName; - builder.Port = ManagedServicePort; - builder.Path = "/oauth2/token"; + var builder = new UriBuilder + { + Scheme = "http", + Host = ManagedServiceHostName, + Port = ManagedServicePort, + Path = "/oauth2/token" + }; - string msiSecret = this.IsBound(nameof(ManagedServiceSecret)) + var msiSecret = this.IsBound(nameof(ManagedServiceSecret)) ? ManagedServiceSecret.ConvertToString() : System.Environment.GetEnvironmentVariable(MSISecretVariable); - string suppliedUri = this.IsBound(nameof(ManagedServiceHostName)) + var suppliedUri = this.IsBound(nameof(ManagedServiceHostName)) ? builder.Uri.ToString() : System.Environment.GetEnvironmentVariable(MSIEndpointVariable); @@ -266,9 +269,10 @@ public override void ExecuteCmdlet() if (!string.IsNullOrEmpty(TenantId)) { - azureAccount.SetProperty(AzureAccount.Property.Tenants, new[] { TenantId }); + azureAccount.SetProperty(AzureAccount.Property.Tenants, TenantId); } +// TODO: Remove IfDef #if NETSTANDARD if (azureAccount.Type == AzureAccount.AccountType.ServicePrincipal) { @@ -299,19 +303,19 @@ public override void ExecuteCmdlet() subscriptionName, password, SkipValidation, - (s) => WriteWarning(s), + WriteWarning, name, - !this.SkipContextPopulation.IsPresent)); + !SkipContextPopulation.IsPresent)); }); } } - bool CheckForExistingContext(AzureRmProfile profile, string name) + private static bool CheckForExistingContext(AzureRmProfile profile, string name) { - return name != null && profile != null && profile.Contexts != null && profile.Contexts.ContainsKey(name); + return name != null && profile?.Contexts != null && profile.Contexts.ContainsKey(name); } - void SetContextWithOverwritePrompt(Action setContextAction) + private void SetContextWithOverwritePrompt(Action setContextAction) { string name = null; if (MyInvocation.BoundParameters.ContainsKey(nameof(ContextName))) @@ -319,7 +323,7 @@ void SetContextWithOverwritePrompt(ActionAn instance of the X509Certificate public static X509Certificate2 NewX509Certificate2(byte[] rawData, string password, X509KeyStorageFlags keyStorageFlags, bool shouldValidatePfx) { - string temporaryFileName = Path.GetTempFileName(); + var temporaryFileName = Path.GetTempFileName(); try { - X509ContentType contentType = X509Certificate2.GetCertContentType(rawData); + var contentType = X509Certificate2.GetCertContentType(rawData); File.WriteAllBytes(temporaryFileName, rawData); return new X509Certificate2(temporaryFileName, password, keyStorageFlags); } @@ -154,8 +156,7 @@ public static X509Certificate2 NewX509Certificate2(byte[] rawData, string passwo public static string GetCertInBase64EncodedForm(string certFileName) { FileStream fileStream = null; - byte[] data = null; - string certInBase64EncodedForm = null; + string certInBase64EncodedForm; try { @@ -167,12 +168,12 @@ public static string GetCertInBase64EncodedForm(string certFileName) throw new Exception("The Certficate size exceeds 1MB. Please provide a file whose size is utmost 1 MB"); } - int size = (int)fileStream.Length; - data = new byte[size]; + var size = (int)fileStream.Length; + var data = new byte[size]; size = fileStream.Read(data, 0, size); // Check if the file is a valid certificate before sending it to service - X509Certificate2 x509 = new X509Certificate2(); + var x509 = new X509Certificate2(); x509.Import(data); if (string.IsNullOrEmpty(x509.Thumbprint)) { @@ -188,10 +189,7 @@ public static string GetCertInBase64EncodedForm(string certFileName) } finally { - if (null != fileStream) - { - fileStream.Close(); - } + fileStream?.Close(); } return certInBase64EncodedForm; @@ -205,7 +203,7 @@ public static string GetCertInBase64EncodedForm(string certFileName) /// friendly name private static string GenerateCertFriendlyName(string subscriptionId, string prefix = "") { - string dateString = DateTime.Now.ToString("M-d-yyyy"); + var dateString = DateTime.Now.ToString("M-d-yyyy"); if (TestMockSupport.RunningMocked) { dateString = string.Empty; @@ -220,6 +218,7 @@ private static string GenerateCertFriendlyName(string subscriptionId, string pre /// A 2048 bit RSA key private static CngKey Create2048RsaKey() { +// TODO: Remove IfDef code #if !NETSTANDARD var keyCreationParameters = new CngKeyCreationParameters { diff --git a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Vault/GetAzureRMRecoveryServicesVaultSettingsFile.cs b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Vault/GetAzureRMRecoveryServicesVaultSettingsFile.cs index 0ebaea9f67f7..683d7ed93ac4 100644 --- a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Vault/GetAzureRMRecoveryServicesVaultSettingsFile.cs +++ b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/Vault/GetAzureRMRecoveryServicesVaultSettingsFile.cs @@ -74,6 +74,7 @@ public class GetAzureRmRecoveryServicesVaultSettingsFile : RecoveryServicesCmdle [Parameter(Position = 2)] public string Path { get; set; } +// TODO: Remove IfDef #if NETSTANDARD /// /// Gets or sets Site Identifier. @@ -108,8 +109,8 @@ public class GetAzureRmRecoveryServicesVaultSettingsFile : RecoveryServicesCmdle [Parameter(ParameterSetName = ARSParameterSets.ForSiteWithCertificate, Mandatory = false)] public SwitchParameter SiteRecovery { - get { return siteRecovery; } - set { siteRecovery = value; } + get { return _siteRecovery; } + set { _siteRecovery = value; } } /// @@ -121,8 +122,8 @@ public SwitchParameter SiteRecovery [Parameter(ParameterSetName = ARSParameterSets.ForBackupVaultTypeWithCertificate, Mandatory = true)] public SwitchParameter Backup { - get { return backup; } - set { backup = value; } + get { return _backup; } + set { _backup = value; } } #else /// @@ -149,8 +150,8 @@ public SwitchParameter Backup [Parameter(ParameterSetName = ARSParameterSets.ForSite, Mandatory = false)] public SwitchParameter SiteRecovery { - get { return siteRecovery; } - set { siteRecovery = value; } + get { return _siteRecovery; } + set { _siteRecovery = value; } } /// @@ -162,13 +163,13 @@ public SwitchParameter SiteRecovery [Parameter(ParameterSetName = ARSParameterSets.ForBackupVaultType, Mandatory = true)] public SwitchParameter Backup { - get { return backup; } - set { backup = value; } + get { return _backup; } + set { _backup = value; } } #endif - private bool siteRecovery; - private bool backup; + private bool _siteRecovery; + private bool _backup; #endregion Parameters @@ -179,19 +180,18 @@ public override void ExecuteCmdlet() { try { +// TODO: Remove IfDef #if NETSTANDARD - if (backup) - { - this.GetBackupCredentialsWithCertificate(Certificate); - } - else - { - this.GetSiteRecoveryCredentialsWithCertificate(Certificate); - } - + if (_backup) + { + GetBackupCredentialsWithCertificate(Certificate); + } + else + { + GetSiteRecoveryCredentialsWithCertificate(Certificate); + } #else - - if (backup) + if (_backup) { this.GetAzureRMRecoveryServicesVaultBackupCredentials(); } @@ -199,15 +199,14 @@ public override void ExecuteCmdlet() { this.GetSiteRecoveryCredentials(); } - #endif } catch (AggregateException aggregateEx) { // if an exception is thrown from a task, it will be wrapped in AggregateException // and propagated to the main thread. Just throwing the first exception in the list. - Exception exception = aggregateEx.InnerExceptions.First(); - this.HandleException(exception); + var exception = aggregateEx.InnerExceptions.First(); + HandleException(exception); } } @@ -217,39 +216,39 @@ public override void ExecuteCmdlet() private void GetBackupCredentialsWithCertificate(string certificate) { // for .netStandard - string targetLocation = string.IsNullOrEmpty(this.Path) ? Utilities.GetDefaultPath() : this.Path; + var targetLocation = string.IsNullOrEmpty(Path) ? Utilities.GetDefaultPath() : Path; if (!Directory.Exists(targetLocation)) { throw new ArgumentException(Resources.VaultCredPathException); } - string subscriptionId = DefaultContext.Subscription.Id.ToString(); - string displayName = this.Vault.Name; + var subscriptionId = DefaultContext.Subscription.Id; + var displayName = Vault.Name; WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.ExecutingGetVaultCredCmdlet, - subscriptionId, this.Vault.ResourceGroupName, this.Vault.Name, targetLocation)); + subscriptionId, Vault.ResourceGroupName, Vault.Name, targetLocation)); - VaultCertificateResponse vaultCertificateResponse = null; - string channelIntegrityKey = string.Empty; + VaultCertificateResponse vaultCertificateResponse; + var channelIntegrityKey = string.Empty; try { // Upload cert into ID Mgmt WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.UploadingCertToIdmgmt)); - byte[] bytes = Encoding.ASCII.GetBytes(certificate); - var certificateArgs = new CertificateRequest(); - certificateArgs.Properties = new RawCertificateData(); - certificateArgs.Properties.Certificate = bytes; - certificateArgs.Properties.AuthType = AuthType.AAD; + var bytes = Encoding.ASCII.GetBytes(certificate); + var certificateArgs = new CertificateRequest + { + Properties = new RawCertificateData {Certificate = bytes, AuthType = AuthType.AAD} + }; - string dateString = DateTime.Now.ToString("M-d-yyyy"); + var dateString = DateTime.Now.ToString("M-d-yyyy"); - var friendlyName = string.Format("{0}{1}-{2}-vaultcredentials", this.Vault.Name, subscriptionId, dateString); + var friendlyName = string.Format("{0}{1}-{2}-vaultcredentials", Vault.Name, subscriptionId, dateString); vaultCertificateResponse = RecoveryServicesClient.GetRecoveryServicesClient.VaultCertificates.CreateWithHttpMessagesAsync( - this.Vault.ResourceGroupName, - this.Vault.Name, + Vault.ResourceGroupName, + Vault.Name, friendlyName, certificateArgs, RecoveryServicesClient.GetRequestHeaders()).Result.Body; @@ -261,7 +260,7 @@ private void GetBackupCredentialsWithCertificate(string certificate) } // generate vault credentials - string vaultCredsFileContent = GenerateVaultCredsForBackup(certificate, subscriptionId, vaultCertificateResponse); + var vaultCredsFileContent = GenerateVaultCredsForBackup(certificate, subscriptionId, vaultCertificateResponse); // NOTE: One of the scenarios for this cmdlet is to generate a file which will be an input // to DPM servers. @@ -271,13 +270,13 @@ private void GetBackupCredentialsWithCertificate(string certificate) "Microsoft.Azure.Portal.RecoveryServices.Models.Common"); // prepare for download - string fileName = string.Format("{0}_{1:ddd MMM dd yyyy}.VaultCredentials", displayName, DateTime.UtcNow); - string filePath = System.IO.Path.Combine(targetLocation, fileName); + var fileName = string.Format("{0}_{1:ddd MMM dd yyyy}.VaultCredentials", displayName, DateTime.UtcNow); + var filePath = System.IO.Path.Combine(targetLocation, fileName); WriteDebug(string.Format(Resources.SavingVaultCred, filePath)); AzureSession.Instance.DataStore.WriteFile(filePath, Encoding.UTF8.GetBytes(vaultCredsFileContent)); - VaultSettingsFilePath output = new VaultSettingsFilePath() + var output = new VaultSettingsFilePath { FilePath = filePath, }; @@ -288,43 +287,42 @@ private void GetBackupCredentialsWithCertificate(string certificate) private void GetSiteRecoveryCredentialsWithCertificate(string certificate) { - string subscriptionId = DefaultContext.Subscription.Id.ToString(); - VaultCertificateResponse vaultCertificateResponse = null; - ASRSite site = new ASRSite(); + var subscriptionId = DefaultContext.Subscription.Id; + var site = new ASRSite(); - if (!string.IsNullOrEmpty(this.SiteIdentifier) - && !string.IsNullOrEmpty(this.SiteFriendlyName)) + if (!string.IsNullOrEmpty(SiteIdentifier) + && !string.IsNullOrEmpty(SiteFriendlyName)) { - site.ID = this.SiteIdentifier; - site.Name = this.SiteFriendlyName; + site.ID = SiteIdentifier; + site.Name = SiteFriendlyName; } try { - string fileName = this.GenerateFileName(); + var fileName = GenerateFileName(); - string filePath = string.IsNullOrEmpty(this.Path) ? Utilities.GetDefaultPath() : this.Path; - string fullFilePath = System.IO.Path.Combine(filePath, fileName); + var filePath = string.IsNullOrEmpty(Path) ? Utilities.GetDefaultPath() : Path; + var fullFilePath = System.IO.Path.Combine(filePath, fileName); // Upload cert into ID Mgmt WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.UploadingCertToIdmgmt)); - byte[] bytes = Encoding.ASCII.GetBytes(certificate); - var certificateArgs = new CertificateRequest(); - certificateArgs.Properties = new RawCertificateData(); - certificateArgs.Properties.Certificate = bytes; - certificateArgs.Properties.AuthType = AuthType.AAD; + var bytes = Encoding.ASCII.GetBytes(certificate); + var certificateArgs = new CertificateRequest + { + Properties = new RawCertificateData {Certificate = bytes, AuthType = AuthType.AAD} + }; - string dateString = DateTime.Now.ToString("M-d-yyyy"); + var dateString = DateTime.Now.ToString("M-d-yyyy"); - var friendlyName = string.Format("{0}{1}-{2}-vaultcredentials", this.Vault.Name, subscriptionId, dateString); - vaultCertificateResponse = RecoveryServicesClient.GetRecoveryServicesClient.VaultCertificates.CreateWithHttpMessagesAsync( - this.Vault.ResourceGroupName, - this.Vault.Name, + var friendlyName = string.Format("{0}{1}-{2}-vaultcredentials", Vault.Name, subscriptionId, dateString); + var vaultCertificateResponse = RecoveryServicesClient.GetRecoveryServicesClient.VaultCertificates.CreateWithHttpMessagesAsync( + Vault.ResourceGroupName, + Vault.Name, friendlyName, certificateArgs, RecoveryServicesClient.GetRequestHeaders()).Result.Body; WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.UploadedCertToIdmgmt)); - string vaultCredsFileContent = GenerateVaultCredsForSiteRecovery( + var vaultCredsFileContent = GenerateVaultCredsForSiteRecovery( certificate, subscriptionId, vaultCertificateResponse, @@ -334,7 +332,7 @@ private void GetSiteRecoveryCredentialsWithCertificate(string certificate) AzureSession.Instance.DataStore.WriteFile(fullFilePath, Encoding.UTF8.GetBytes(vaultCredsFileContent)); - VaultSettingsFilePath output = new VaultSettingsFilePath() + var output = new VaultSettingsFilePath { FilePath = fullFilePath, }; @@ -353,58 +351,58 @@ private void GetSiteRecoveryCredentialsWithCertificate(string certificate) /// private void GetSiteRecoveryCredentials() { - IAzureSubscription subscription = DefaultProfile.DefaultContext.Subscription; + var subscription = DefaultProfile.DefaultContext.Subscription; // Generate certificate - X509Certificate2 cert = CertUtils.CreateSelfSignedCertificate( + var cert = CertUtils.CreateSelfSignedCertificate( VaultCertificateExpiryInHoursForHRM, - subscription.Id.ToString(), - this.Vault.Name); + subscription.Id, + Vault.Name); - ASRSite site = new ASRSite(); + var site = new ASRSite(); - if (!string.IsNullOrEmpty(this.SiteIdentifier) - && !string.IsNullOrEmpty(this.SiteFriendlyName)) + if (!string.IsNullOrEmpty(SiteIdentifier) + && !string.IsNullOrEmpty(SiteFriendlyName)) { - site.ID = this.SiteIdentifier; - site.Name = this.SiteFriendlyName; + site.ID = SiteIdentifier; + site.Name = SiteFriendlyName; } - string fileName = this.GenerateFileName(); + var fileName = GenerateFileName(); - string filePath = string.IsNullOrEmpty(this.Path) ? Utilities.GetDefaultPath() : this.Path; + var filePath = string.IsNullOrEmpty(Path) ? Utilities.GetDefaultPath() : Path; // Generate file. - if (RecoveryServicesClient.getVaultAuthType(this.Vault.ResourceGroupName, this.Vault.Name) == 0) + if (RecoveryServicesClient.getVaultAuthType(Vault.ResourceGroupName, Vault.Name) == 0) { - ASRVaultCreds vaultCreds = RecoveryServicesClient.GenerateVaultCredential( + var vaultCreds = RecoveryServicesClient.GenerateVaultCredential( cert, - this.Vault, + Vault, site, AuthType.ACS); // write the content to a file. - VaultSettingsFilePath output = new VaultSettingsFilePath() + var output = new VaultSettingsFilePath { - FilePath = Utilities.WriteToFile(vaultCreds, filePath, fileName) + FilePath = Utilities.WriteToFile(vaultCreds, filePath, fileName) }; // print the path to the user. - this.WriteObject(output, true); + WriteObject(output, true); } else { - string fullFilePath = System.IO.Path.Combine(filePath, fileName); + var fullFilePath = System.IO.Path.Combine(filePath, fileName); WriteDebug( string.Format( CultureInfo.InvariantCulture, Resources.ExecutingGetVaultCredCmdlet, subscription.Id, - this.Vault.ResourceGroupName, - this.Vault.Name, + Vault.ResourceGroupName, + Vault.Name, fullFilePath)); - VaultCertificateResponse vaultCertificateResponse = null; + VaultCertificateResponse vaultCertificateResponse; try { // Upload cert into ID Mgmt @@ -412,9 +410,9 @@ private void GetSiteRecoveryCredentials() vaultCertificateResponse = UploadCert(cert); WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.UploadedCertToIdmgmt)); - string managementCert = CertUtils.SerializeCert(cert, X509ContentType.Pfx); + var managementCert = CertUtils.SerializeCert(cert, X509ContentType.Pfx); // generate vault credentials - string vaultCredsFileContent = GenerateVaultCredsForSiteRecovery( + var vaultCredsFileContent = GenerateVaultCredsForSiteRecovery( managementCert, subscription.Id, vaultCertificateResponse, @@ -424,7 +422,7 @@ private void GetSiteRecoveryCredentials() AzureSession.Instance.DataStore.WriteFile(fullFilePath, Encoding.UTF8.GetBytes(vaultCredsFileContent)); - VaultSettingsFilePath output = new VaultSettingsFilePath() + var output = new VaultSettingsFilePath { FilePath = fullFilePath, }; @@ -446,15 +444,15 @@ private void GetSiteRecoveryCredentials() private string GenerateFileName() { string fileName; - string format = "yyyy-MM-ddTHH-mm-ss"; + const string format = "yyyy-MM-ddTHH-mm-ss"; - if (string.IsNullOrEmpty(this.SiteIdentifier) || string.IsNullOrEmpty(this.SiteFriendlyName)) + if (string.IsNullOrEmpty(SiteIdentifier) || string.IsNullOrEmpty(SiteFriendlyName)) { - fileName = string.Format("{0}_{1}.VaultCredentials", this.Vault.Name, DateTime.UtcNow.ToString(format)); + fileName = string.Format("{0}_{1}.VaultCredentials", Vault.Name, DateTime.UtcNow.ToString(format)); } else { - fileName = string.Format("{0}_{1}_{2}.VaultCredentials", this.SiteFriendlyName, this.Vault.Name, DateTime.UtcNow.ToString(format)); + fileName = string.Format("{0}_{1}_{2}.VaultCredentials", SiteFriendlyName, Vault.Name, DateTime.UtcNow.ToString(format)); } return fileName; @@ -466,25 +464,25 @@ private string GenerateFileName() /// public void GetAzureRMRecoveryServicesVaultBackupCredentials() { - string targetLocation = string.IsNullOrEmpty(this.Path) ? Utilities.GetDefaultPath() : this.Path; + var targetLocation = string.IsNullOrEmpty(Path) ? Utilities.GetDefaultPath() : Path; if (!Directory.Exists(targetLocation)) { throw new ArgumentException(Resources.VaultCredPathException); } - string subscriptionId = DefaultContext.Subscription.Id.ToString(); - string displayName = this.Vault.Name; + var subscriptionId = DefaultContext.Subscription.Id; + var displayName = Vault.Name; WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.ExecutingGetVaultCredCmdlet, - subscriptionId, this.Vault.ResourceGroupName, this.Vault.Name, targetLocation)); + subscriptionId, Vault.ResourceGroupName, Vault.Name, targetLocation)); // Generate certificate - X509Certificate2 cert = CertUtils.CreateSelfSignedCertificate( - VaultCertificateExpiryInHoursForBackup, subscriptionId.ToString(), this.Vault.Name); + var cert = CertUtils.CreateSelfSignedCertificate( + VaultCertificateExpiryInHoursForBackup, subscriptionId, Vault.Name); - VaultCertificateResponse vaultCertificateResponse = null; - string channelIntegrityKey = string.Empty; + VaultCertificateResponse vaultCertificateResponse; + var channelIntegrityKey = string.Empty; try { // Upload cert into ID Mgmt @@ -498,7 +496,7 @@ public void GetAzureRMRecoveryServicesVaultBackupCredentials() } // generate vault credentials - string vaultCredsFileContent = GenerateVaultCreds(cert, subscriptionId, vaultCertificateResponse); + var vaultCredsFileContent = GenerateVaultCreds(cert, subscriptionId, vaultCertificateResponse); // NOTE: One of the scenarios for this cmdlet is to generate a file which will be an input // to DPM servers. @@ -508,13 +506,13 @@ public void GetAzureRMRecoveryServicesVaultBackupCredentials() "Microsoft.Azure.Portal.RecoveryServices.Models.Common"); // prepare for download - string fileName = string.Format("{0}_{1:ddd MMM dd yyyy}.VaultCredentials", displayName, DateTime.UtcNow); - string filePath = System.IO.Path.Combine(targetLocation, fileName); + var fileName = string.Format("{0}_{1:ddd MMM dd yyyy}.VaultCredentials", displayName, DateTime.UtcNow); + var filePath = System.IO.Path.Combine(targetLocation, fileName); WriteDebug(string.Format(Resources.SavingVaultCred, filePath)); AzureSession.Instance.DataStore.WriteFile(filePath, Encoding.UTF8.GetBytes(vaultCredsFileContent)); - VaultSettingsFilePath output = new VaultSettingsFilePath() + var output = new VaultSettingsFilePath { FilePath = filePath, }; @@ -530,7 +528,7 @@ public void GetAzureRMRecoveryServicesVaultBackupCredentials() /// acs namespace of the uploaded cert private VaultCertificateResponse UploadCert(X509Certificate2 cert) { - return RecoveryServicesClient.UploadCertificate(cert, this.Vault); + return RecoveryServicesClient.UploadCertificate(cert, Vault); } /// @@ -544,7 +542,7 @@ private string GenerateVaultCreds(X509Certificate2 cert, string subscriptionId, { try { - string certString = CertUtils.SerializeCert(cert, X509ContentType.Pfx); + var certString = CertUtils.SerializeCert(cert, X509ContentType.Pfx); return GenerateVaultCredsForBackup(certString, subscriptionId, vaultCertificateResponse); } catch (Exception exception) @@ -567,9 +565,9 @@ private string GenerateVaultCredsForBackup(string certificateString, string subs { using (var writer = XmlWriter.Create(output, GetXmlWriterSettings())) { - ResourceCertificateAndAadDetails aadDetails = vaultCertificateResponse.Properties as ResourceCertificateAndAadDetails; + var aadDetails = vaultCertificateResponse.Properties as ResourceCertificateAndAadDetails; - RSBackupVaultAADCreds vaultCreds = new RSBackupVaultAADCreds() + var vaultCreds = new RSBackupVaultAADCreds { SubscriptionId = subscriptionId, ResourceName = Vault.Name, @@ -587,7 +585,7 @@ private string GenerateVaultCredsForBackup(string certificateString, string subs AgentLinks = GetAgentLinks() }; - DataContractSerializer serializer = new DataContractSerializer(typeof(RSBackupVaultAADCreds)); + var serializer = new DataContractSerializer(typeof(RSBackupVaultAADCreds)); serializer.WriteObject(writer, vaultCreds); WriteDebug(string.Format(CultureInfo.InvariantCulture, Resources.BackupVaultSerialized)); @@ -612,11 +610,11 @@ private string GenerateVaultCredsForSiteRecovery(string managementCert, string s { using (var writer = XmlWriter.Create(output, GetXmlWriterSettings())) { - ResourceCertificateAndAadDetails aadDetails = vaultCertificateResponse.Properties as ResourceCertificateAndAadDetails; - string resourceProviderNamespace = string.Empty; - string resourceType = string.Empty; + var aadDetails = vaultCertificateResponse.Properties as ResourceCertificateAndAadDetails; + var resourceProviderNamespace = string.Empty; + var resourceType = string.Empty; - Utilities.GetResourceProviderNamespaceAndType(this.Vault.ID, out resourceProviderNamespace, out resourceType); + Utilities.GetResourceProviderNamespaceAndType(Vault.ID, out resourceProviderNamespace, out resourceType); Logger.Instance.WriteDebug(string.Format( "GenerateVaultCredential resourceProviderNamespace = {0}, resourceType = {1}", @@ -624,28 +622,28 @@ private string GenerateVaultCredsForSiteRecovery(string managementCert, string s resourceType)); // Update vault settings with the working vault to generate file - Utilities.UpdateCurrentVaultContext(new ASRVaultCreds() + Utilities.UpdateCurrentVaultContext(new ASRVaultCreds { - ResourceGroupName = this.Vault.ResourceGroupName, - ResourceName = this.Vault.Name, + ResourceGroupName = Vault.ResourceGroupName, + ResourceName = Vault.Name, ResourceNamespace = resourceProviderNamespace, ARMResourceType = resourceType }); //Code taken from Ibiza code - string aadAudience = string.Format(CultureInfo.InvariantCulture, + var aadAudience = string.Format(CultureInfo.InvariantCulture, @"https://RecoveryServiceVault/{0}/{1}/{2}", Vault.Location, Vault.Name, aadDetails.ResourceId); - RSVaultAsrCreds vaultCreds = new RSVaultAsrCreds() + var vaultCreds = new RSVaultAsrCreds { VaultDetails = new ASRVaultDetails { SubscriptionId = subscriptionId, - ResourceGroup = this.Vault.ResourceGroupName, - ResourceName = this.Vault.Name, + ResourceGroup = Vault.ResourceGroupName, + ResourceName = Vault.Name, ResourceId = aadDetails.ResourceId.Value, Location = Vault.Location, ResourceType = RecoveryServicesVaultType, @@ -661,12 +659,12 @@ private string GenerateVaultCredsForSiteRecovery(string managementCert, string s AadVaultAudience = aadAudience, ArmManagementEndpoint = aadDetails.AzureManagementEndpointAudience }, - ChannelIntegrityKey = this.RecoveryServicesClient.GetCurrentVaultChannelIntegrityKey(), - SiteId = asrSite.ID == null ? String.Empty : asrSite.ID, - SiteName = asrSite.Name == null ? String.Empty : asrSite.Name + ChannelIntegrityKey = RecoveryServicesClient.GetCurrentVaultChannelIntegrityKey(), + SiteId = asrSite.ID ?? String.Empty, + SiteName = asrSite.Name ?? String.Empty }; - DataContractSerializer serializer = new DataContractSerializer(typeof(RSVaultAsrCreds)); + var serializer = new DataContractSerializer(typeof(RSVaultAsrCreds)); serializer.WriteObject(writer, vaultCreds); } @@ -690,7 +688,7 @@ private static string GetAgentLinks() /// A set of XmlWriterSettings to use for the publishing profile /// /// The XmlWriterSettings set - private XmlWriterSettings GetXmlWriterSettings() + private static XmlWriterSettings GetXmlWriterSettings() { return new XmlWriterSettings { From 4b009e286c18f7180d48bb432ac433a678c23d06 Mon Sep 17 00:00:00 2001 From: MiYanni Date: Mon, 29 Oct 2018 16:58:04 -0700 Subject: [PATCH 07/15] Updated Resources and Sql #if statements. --- .../Cmdlets/Components/ApiVersionHelper.cs | 52 +++---- ...pertyNamesWithOverridesContractResolver.cs | 16 +- .../ResourcesBaseCmdlet.cs | 62 +++----- .../ResourcesExtensions.cs | 135 ++++++++--------- .../Common/AzureEndpointsCommunicator.cs | 143 ++++++++---------- .../BaseSqlVulnerabilityAssessmentAdapter.cs | 16 +- ...rabilityAssessmentEndpointsCommunicator.cs | 17 +-- 7 files changed, 201 insertions(+), 240 deletions(-) diff --git a/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs b/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs index d2f3382799e7..39431878e712 100644 --- a/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs +++ b/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs @@ -19,19 +19,20 @@ namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Components using Commands.Common.Authentication.Abstractions; using Entities.Providers; using Extensions; - using Microsoft.Azure.Commands.ResourceManager.Common; + using Common; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; -#if !NETSTANDARD - using System.Runtime.Caching; -#else +// TODO: Remove IfDef +#if NETSTANDARD using Microsoft.Extensions.Caching.Memory; -#endif - +#else + using System.Runtime.Caching; +#endif + /// /// Helper class for determining the API version /// @@ -52,7 +53,7 @@ internal static Task DetermineApiVersion(IAzureContext context, string r var resourceType = ResourceIdUtility.GetExtensionResourceType(resourceId: resourceId, includeProviderNamespace: false) ?? ResourceIdUtility.GetResourceType(resourceId: resourceId, includeProviderNamespace: false); - return ApiVersionHelper.DetermineApiVersion(context: context, providerNamespace: providerNamespace, resourceType: resourceType, cancellationToken: cancellationToken, pre: pre, cmdletHeaderValues: cmdletHeaderValues); + return DetermineApiVersion(context: context, providerNamespace: providerNamespace, resourceType: resourceType, cancellationToken: cancellationToken, pre: pre, cmdletHeaderValues: cmdletHeaderValues); } /// @@ -67,7 +68,7 @@ internal static Task DetermineApiVersion(IAzureContext context, string p { var cacheKey = ApiVersionCache.GetCacheKey(context.Environment.Name, providerNamespace: providerNamespace, resourceType: resourceType); var apiVersions = ApiVersionCache.Instance - .AddOrGetExisting(cacheKey: cacheKey, getFreshData: () => ApiVersionHelper.GetApiVersionsForResourceType( + .AddOrGetExisting(cacheKey: cacheKey, getFreshData: () => GetApiVersionsForResourceType( context, providerNamespace: providerNamespace, resourceType: resourceType, @@ -141,14 +142,15 @@ private static string[] GetApiVersionsForResourceType(IAzureContext context, str [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Instances of this type are meant to be singletons.")] private class ApiVersionCache { - private static MemoryCache _cache; + private static readonly MemoryCache Cache; static ApiVersionCache() { -#if !NETSTANDARD - _cache = MemoryCache.Default; +// TODO: Remove IfDef +#if NETSTANDARD + Cache = new MemoryCache(new MemoryCacheOptions()); #else - _cache = new MemoryCache(new MemoryCacheOptions()); + _cache = MemoryCache.Default; #endif } /// @@ -167,7 +169,7 @@ static ApiVersionCache() /// The polling interval. private ApiVersionCache(TimeSpan cacheDataExpirationTime) { - this.CacheDataExpirationTime = cacheDataExpirationTime; + CacheDataExpirationTime = cacheDataExpirationTime; } /// @@ -179,21 +181,19 @@ internal string[] AddOrGetExisting(string cacheKey, Func getFreshData) { cacheKey = cacheKey.ToUpperInvariant(); - var cacheItem = this.GetCacheItem(cacheKey: cacheKey); + var cacheItem = GetCacheItem(cacheKey: cacheKey); + if (cacheItem != null) return cacheItem; - if (cacheItem == null) - { - var expirationTime = DateTime.UtcNow.Add(this.CacheDataExpirationTime); + var expirationTime = DateTime.UtcNow.Add(CacheDataExpirationTime); - cacheItem = getFreshData(); + cacheItem = getFreshData(); - if (cacheItem != null) - { - this.SetCacheItem( + if (cacheItem != null) + { + SetCacheItem( cacheKey: cacheKey, data: cacheItem, absoluteExpirationTime: expirationTime); - } } return cacheItem; @@ -203,9 +203,9 @@ internal string[] AddOrGetExisting(string cacheKey, Func getFreshData) /// Gets the cache entry. /// /// The cache key. - private string[] GetCacheItem(string cacheKey) + private static string[] GetCacheItem(string cacheKey) { - return _cache.Get(cacheKey).Cast(); + return Cache.Get(cacheKey).Cast(); } /// @@ -214,9 +214,9 @@ private string[] GetCacheItem(string cacheKey) /// The cache key. /// The data to add. /// The absolute expiration time. - private void SetCacheItem(string cacheKey, string[] data, DateTimeOffset absoluteExpirationTime) + private static void SetCacheItem(string cacheKey, string[] data, DateTimeOffset absoluteExpirationTime) { - _cache.Set(key: cacheKey, value: data, absoluteExpiration: absoluteExpirationTime); + Cache.Set(key: cacheKey, value: data, absoluteExpiration: absoluteExpirationTime); } /// diff --git a/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Json/CamelCasePropertyNamesWithOverridesContractResolver.cs b/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Json/CamelCasePropertyNamesWithOverridesContractResolver.cs index 84c814d1bb27..42e6c3737ce1 100644 --- a/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Json/CamelCasePropertyNamesWithOverridesContractResolver.cs +++ b/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Json/CamelCasePropertyNamesWithOverridesContractResolver.cs @@ -35,13 +35,12 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ var result = base.CreateProperty(member, memberSerialization); var attributes = member.GetCustomAttributes(attributeType: typeof(JsonPropertyAttribute), inherit: true); - if (attributes.Any()) + if (!attributes.Any()) return result; + + var propertyName = attributes.Cast().Single().PropertyName; + if (!string.IsNullOrEmpty(propertyName)) { - var propertyName = attributes.Cast().Single().PropertyName; - if (!string.IsNullOrEmpty(propertyName)) - { - result.PropertyName = propertyName; - } + result.PropertyName = propertyName; } return result; @@ -55,13 +54,14 @@ protected override JsonDictionaryContract CreateDictionaryContract(Type objectTy { var contract = base.CreateDictionaryContract(objectType); +// TODO: Remove IfDef code +#if !NETSTANDARD var attributes = objectType.GetCustomAttributes(attributeType: typeof(JsonPreserveCaseDictionaryAttribute), inherit: true); if (attributes.Any()) { -#if !NETSTANDARD contract.PropertyNameResolver = propertyName => propertyName; -#endif } +#endif return contract; } diff --git a/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesBaseCmdlet.cs b/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesBaseCmdlet.cs index 7fbfeaae4ad3..1b1c5417d328 100644 --- a/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesBaseCmdlet.cs +++ b/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesBaseCmdlet.cs @@ -15,7 +15,7 @@ namespace Microsoft.Azure.Commands.Resources.Models { - using Microsoft.Azure.Commands.Resources.Models.Authorization; + using Authorization; using ResourceManager.Common; /// @@ -26,24 +26,25 @@ public abstract class ResourcesBaseCmdlet : AzureRMCmdlet /// /// Field that holds the resource client instance /// - private ResourcesClient resourcesClient; + private ResourcesClient _resourcesClient; +// TODO: Remove IfDef code #if !NETSTANDARD /// /// Field that holds the gallery templates client instance /// - private GalleryTemplatesClient galleryTemplatesClient; + private GalleryTemplatesClient _galleryTemplatesClient; #endif /// /// Field that holds the policies client instance /// - private AuthorizationClient policiesClient; + private AuthorizationClient _policiesClient; /// - /// Field that holds the subscripotions client instance + /// Field that holds the subscriptions client instance /// - private SubscriptionsClient subscriptionsClient; + private SubscriptionsClient _subscriptionsClient; /// /// Gets or sets the resources client @@ -52,21 +53,18 @@ public ResourcesClient ResourcesClient { get { - if (this.resourcesClient == null) + return _resourcesClient ?? (_resourcesClient = new ResourcesClient(DefaultContext) { - this.resourcesClient = new ResourcesClient(DefaultContext) - { - VerboseLogger = WriteVerboseWithTimestamp, - ErrorLogger = WriteErrorWithTimestamp, - WarningLogger = WriteWarningWithTimestamp - }; - } - return this.resourcesClient; + VerboseLogger = WriteVerboseWithTimestamp, + ErrorLogger = WriteErrorWithTimestamp, + WarningLogger = WriteWarningWithTimestamp + }); } - set { this.resourcesClient = value; } + set { _resourcesClient = value; } } +// TODO: Remove IfDef code #if !NETSTANDARD /// /// Gets or sets the gallery templates client @@ -75,17 +73,17 @@ public GalleryTemplatesClient GalleryTemplatesClient { get { - if (this.galleryTemplatesClient == null) + if (_galleryTemplatesClient == null) { // since this accessor can be called before BeginProcessing, use GetCurrentContext if no // profile is passed in - this.galleryTemplatesClient = new GalleryTemplatesClient(DefaultContext); + _galleryTemplatesClient = new GalleryTemplatesClient(DefaultContext); } - return this.galleryTemplatesClient; + return _galleryTemplatesClient; } - set { this.galleryTemplatesClient = value; } + set { _galleryTemplatesClient = value; } } #endif @@ -94,16 +92,9 @@ public GalleryTemplatesClient GalleryTemplatesClient /// public AuthorizationClient PoliciesClient { - get - { - if (this.policiesClient == null) - { - this.policiesClient = new AuthorizationClient(DefaultContext); - } - return this.policiesClient; - } + get { return _policiesClient ?? (_policiesClient = new AuthorizationClient(DefaultContext)); } - set { this.policiesClient = value; } + set { _policiesClient = value; } } /// @@ -111,16 +102,9 @@ public AuthorizationClient PoliciesClient /// public SubscriptionsClient SubscriptionsClient { - get - { - if (this.subscriptionsClient == null) - { - this.subscriptionsClient = new SubscriptionsClient(DefaultContext); - } - return this.subscriptionsClient; - } + get { return _subscriptionsClient ?? (_subscriptionsClient = new SubscriptionsClient(DefaultContext)); } - set { this.subscriptionsClient = value; } + set { _subscriptionsClient = value; } } /// @@ -128,7 +112,7 @@ public SubscriptionsClient SubscriptionsClient /// public virtual string DetermineParameterSetName() { - return this.ParameterSetName; + return ParameterSetName; } } } diff --git a/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs b/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs index b12015aedb98..6a0bde7b098a 100644 --- a/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs +++ b/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs @@ -27,6 +27,7 @@ using Microsoft.WindowsAzure.Commands.Common; using Microsoft.Azure.Management.ResourceManager.Models; using Microsoft.Azure.Commands.Resources.Models; +// TODO: Remove IfDef code #if !NETSTANDARD using Microsoft.Azure.Commands.Resources.Models.Gallery; #endif @@ -35,6 +36,7 @@ namespace Microsoft.Azure.Commands.Resources.Models { public static class ResourcesExtensions { +// TODO: Remove IfDef code #if !NETSTANDARD public static PSGalleryItem ToPSGalleryItem(this GalleryItem gallery) { @@ -50,7 +52,7 @@ public static PSGalleryItem ToPSGalleryItem(this GalleryItem gallery) public static PSResource ToPSResource(this GenericResource resource, ResourcesClient client, bool minimal) { - ResourceIdentifier identifier = new ResourceIdentifier(resource.Id); + var identifier = new ResourceIdentifier(resource.Id); return new PSResource { Name = identifier.ResourceName, @@ -68,7 +70,7 @@ public static PSResource ToPSResource(this GenericResource resource, ResourcesCl public static PSPermission ToPSPermission(this Permission permission) { - return new PSPermission() + return new PSPermission { Actions = new List(permission.Actions), NotActions = new List(permission.NotActions) @@ -82,7 +84,7 @@ private static string ConstructTemplateLinkView(TemplateLink templateLink) return string.Empty; } - StringBuilder result = new StringBuilder(); + var result = new StringBuilder(); result.AppendLine(); result.AppendLine(string.Format("{0, -15}: {1}", "Uri", templateLink.Uri)); @@ -93,16 +95,14 @@ private static string ConstructTemplateLinkView(TemplateLink templateLink) private static string GetEventDataCaller(Dictionary claims) { - string name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"; + const string name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"; if (claims == null || !claims.ContainsKey(name)) { return null; } - else - { - return claims[name]; - } + + return claims[name]; } public static string ConstructDeploymentVariableTable(Dictionary dictionary) @@ -111,20 +111,19 @@ public static string ConstructDeploymentVariableTable(Dictionary 0) - { - string rowFormat = "{0, -15} {1, -25} {2, -10}\r\n"; - result.AppendLine(); - result.AppendFormat(rowFormat, "Name", "Type", "Value"); - result.AppendFormat(rowFormat, GeneralUtilities.GenerateSeparator(15, "="), GeneralUtilities.GenerateSeparator(25, "="), GeneralUtilities.GenerateSeparator(10, "=")); + var result = new StringBuilder(); + result.AppendLine(); + result.AppendFormat(rowFormat, "Name", "Type", "Value"); + result.AppendFormat(rowFormat, GeneralUtilities.GenerateSeparator(15, "="), GeneralUtilities.GenerateSeparator(25, "="), GeneralUtilities.GenerateSeparator(10, "=")); - foreach (KeyValuePair pair in dictionary) - { - result.AppendFormat(rowFormat, pair.Key, pair.Value.Type, pair.Value.Value); - } + foreach (var pair in dictionary) + { + result.AppendFormat(rowFormat, pair.Key, pair.Value.Type, pair.Value.Value); } return result.ToString(); @@ -138,14 +137,14 @@ public static string ConstructTagsTable(Hashtable tags) return null; } - StringBuilder resourcesTable = new StringBuilder(); + var resourcesTable = new StringBuilder(); var tagsDictionary = TagsConversionHelper.CreateTagDictionary(tags, false); - int maxNameLength = Math.Max("Name".Length, tagsDictionary.Max(tag => tag.Key.Length)); - int maxValueLength = Math.Max("Value".Length, tagsDictionary.Max(tag => tag.Value.Length)); + var maxNameLength = Math.Max("Name".Length, tagsDictionary.Max(tag => tag.Key.Length)); + var maxValueLength = Math.Max("Value".Length, tagsDictionary.Max(tag => tag.Value.Length)); - string rowFormat = "{0, -" + maxNameLength + "} {1, -" + maxValueLength + "}\r\n"; + var rowFormat = "{0, -" + maxNameLength + "} {1, -" + maxValueLength + "}\r\n"; resourcesTable.AppendLine(); resourcesTable.AppendFormat(rowFormat, "Name", "Value"); resourcesTable.AppendFormat(rowFormat, @@ -167,24 +166,22 @@ public static string ConstructTagsTable(Hashtable tags) public static string ConstructPermissionsTable(List permissions) { - StringBuilder permissionsTable = new StringBuilder(); + if (permissions == null || permissions.Count <= 0) return string.Empty; - if (permissions != null && permissions.Count > 0) - { - int maxActionsLength = Math.Max("Actions".Length, permissions.Where(p => p.Actions != null).DefaultIfEmpty(EmptyPermission).Max(p => p.ActionsString.Length)); - int maxNotActionsLength = Math.Max("NotActions".Length, permissions.Where(p => p.NotActions != null).DefaultIfEmpty(EmptyPermission).Max(p => p.NotActionsString.Length)); + var maxActionsLength = Math.Max("Actions".Length, permissions.Where(p => p.Actions != null).DefaultIfEmpty(EmptyPermission).Max(p => p.ActionsString.Length)); + var maxNotActionsLength = Math.Max("NotActions".Length, permissions.Where(p => p.NotActions != null).DefaultIfEmpty(EmptyPermission).Max(p => p.NotActionsString.Length)); - string rowFormat = "{0, -" + maxActionsLength + "} {1, -" + maxNotActionsLength + "}\r\n"; - permissionsTable.AppendLine(); - permissionsTable.AppendFormat(rowFormat, "Actions", "NotActions"); - permissionsTable.AppendFormat(rowFormat, - GeneralUtilities.GenerateSeparator(maxActionsLength, "="), - GeneralUtilities.GenerateSeparator(maxNotActionsLength, "=")); + var rowFormat = "{0, -" + maxActionsLength + "} {1, -" + maxNotActionsLength + "}\r\n"; + var permissionsTable = new StringBuilder(); + permissionsTable.AppendLine(); + permissionsTable.AppendFormat(rowFormat, "Actions", "NotActions"); + permissionsTable.AppendFormat(rowFormat, + GeneralUtilities.GenerateSeparator(maxActionsLength, "="), + GeneralUtilities.GenerateSeparator(maxNotActionsLength, "=")); - foreach (PSPermission permission in permissions) - { - permissionsTable.AppendFormat(rowFormat, permission.ActionsString, permission.NotActionsString); - } + foreach (var permission in permissions) + { + permissionsTable.AppendFormat(rowFormat, permission.ActionsString, permission.NotActionsString); } return permissionsTable.ToString(); @@ -193,7 +190,7 @@ private static PSPermission EmptyPermission { get { - return new PSPermission() + return new PSPermission { Actions = new List(), NotActions = new List() @@ -203,7 +200,7 @@ private static PSPermission EmptyPermission public static PSResourceGroupDeployment ToPSResourceGroupDeployment(this DeploymentExtended result, string resourceGroup) { - PSResourceGroupDeployment deployment = new PSResourceGroupDeployment(); + var deployment = new PSResourceGroupDeployment(); if (result != null) { @@ -215,44 +212,42 @@ public static PSResourceGroupDeployment ToPSResourceGroupDeployment(this Deploym private static PSResourceGroupDeployment CreatePSResourceGroupDeployment( string name, - string gesourceGroup, + string resourceGroup, DeploymentPropertiesExtended properties) { - PSResourceGroupDeployment deploymentObject = new PSResourceGroupDeployment(); + var deploymentObject = new PSResourceGroupDeployment + { + DeploymentName = name, ResourceGroupName = resourceGroup + }; + if (properties == null) return deploymentObject; - deploymentObject.DeploymentName = name; - deploymentObject.ResourceGroupName = gesourceGroup; + deploymentObject.Mode = properties.Mode(); + deploymentObject.Timestamp = properties.Timestamp(); + deploymentObject.ProvisioningState = properties.ProvisioningState; + deploymentObject.TemplateLink = properties.TemplateLink; + deploymentObject.CorrelationId = properties.CorrelationId; + deploymentObject.OnErrorDeployment = properties.OnErrorDeployment; - if (properties != null) + if (properties.DebugSetting() != null && !string.IsNullOrEmpty(properties.DebugSetting().DetailLevel())) { - deploymentObject.Mode = properties.Mode(); - deploymentObject.Timestamp = properties.Timestamp(); - deploymentObject.ProvisioningState = properties.ProvisioningState; - deploymentObject.TemplateLink = properties.TemplateLink; - deploymentObject.CorrelationId = properties.CorrelationId; - deploymentObject.OnErrorDeployment = properties.OnErrorDeployment; - - if (properties.DebugSetting() != null && !string.IsNullOrEmpty(properties.DebugSetting().DetailLevel())) - { - deploymentObject.DeploymentDebugLogLevel = properties.DebugSetting().DetailLevel(); - } + deploymentObject.DeploymentDebugLogLevel = properties.DebugSetting().DetailLevel(); + } - if (properties.Outputs != null && !string.IsNullOrEmpty(properties.Outputs.ToString())) - { - Dictionary outputs = JsonConvert.DeserializeObject>(properties.Outputs.ToString()); - deploymentObject.Outputs = outputs; - } + if (properties.Outputs != null && !string.IsNullOrEmpty(properties.Outputs.ToString())) + { + var outputs = JsonConvert.DeserializeObject>(properties.Outputs.ToString()); + deploymentObject.Outputs = outputs; + } - if (properties.Parameters != null && !string.IsNullOrEmpty(properties.Parameters.ToString())) - { - Dictionary parameters = JsonConvert.DeserializeObject>(properties.Parameters.ToString()); - deploymentObject.Parameters = parameters; - } + if (properties.Parameters != null && !string.IsNullOrEmpty(properties.Parameters.ToString())) + { + var parameters = JsonConvert.DeserializeObject>(properties.Parameters.ToString()); + deploymentObject.Parameters = parameters; + } - if (properties.TemplateLink != null) - { - deploymentObject.TemplateLinkString = ConstructTemplateLinkView(properties.TemplateLink); - } + if (properties.TemplateLink != null) + { + deploymentObject.TemplateLinkString = ConstructTemplateLinkView(properties.TemplateLink); } return deploymentObject; diff --git a/src/ResourceManager/Sql/Commands.Sql/Common/AzureEndpointsCommunicator.cs b/src/ResourceManager/Sql/Commands.Sql/Common/AzureEndpointsCommunicator.cs index 7949127ee276..54a6ce62ba2d 100644 --- a/src/ResourceManager/Sql/Commands.Sql/Common/AzureEndpointsCommunicator.cs +++ b/src/ResourceManager/Sql/Commands.Sql/Common/AzureEndpointsCommunicator.cs @@ -36,7 +36,7 @@ public class AzureEndpointsCommunicator /// /// The storage management client used by this communicator /// - private static Microsoft.Azure.Management.Storage.StorageManagementClient StorageV2Client { get; set; } + private static StorageManagementClient StorageV2Client { get; set; } /// /// Gets or sets the Azure subscription @@ -60,12 +60,11 @@ public class AzureEndpointsCommunicator public AzureEndpointsCommunicator(IAzureContext context) { Context = context; - if (context.Subscription != Subscription) - { - Subscription = context.Subscription; - ResourcesClient = null; - StorageV2Client = null; - } + if (context.Subscription == Subscription) return; + + Subscription = context.Subscription; + ResourcesClient = null; + StorageV2Client = null; } private static class StorageAccountType @@ -77,10 +76,11 @@ private static class StorageAccountType /// /// Lazy creation of a single instance of a storage client /// - public static Microsoft.Azure.Management.Storage.StorageManagementClient GetStorageV2Client(IAzureContext context) + public static StorageManagementClient GetStorageV2Client(IAzureContext context) { +// TODO: Remove IfDef #if NETSTANDARD - return AzureSession.Instance.ClientFactory.CreateArmClient(context, AzureEnvironment.Endpoint.ResourceManager); + return AzureSession.Instance.ClientFactory.CreateArmClient(context, AzureEnvironment.Endpoint.ResourceManager); #else return AzureSession.Instance.ClientFactory.CreateClient(context, AzureEnvironment.Endpoint.ResourceManager); #endif @@ -92,14 +92,14 @@ public static Microsoft.Azure.Management.Storage.StorageManagementClient GetStor /// A dictionary with two entries, one for each possible key type with the appropriate key public async Task> GetStorageKeysAsync(string resourceGroupName, string storageAccountName) { - Management.Storage.StorageManagementClient client = GetCurrentStorageV2Client(); + var client = GetCurrentStorageV2Client(); - string url = Context.Environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager).ToString(); + var url = Context.Environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager).ToString(); if (!url.EndsWith("/")) { url = url + "/"; } - +// TODO: Remove IfDef #if NETSTANDARD url = url + "subscriptions/" + (client.SubscriptionId != null ? client.SubscriptionId.Trim() : ""); #else @@ -109,19 +109,17 @@ public async Task> GetStorageKeysAsync(string url = url + "/providers/Microsoft.ClassicStorage/storageAccounts/" + storageAccountName; url = url + "/listKeys?api-version=2014-06-01"; - HttpRequestMessage httpRequest = new HttpRequestMessage(); - httpRequest.Method = HttpMethod.Post; - httpRequest.RequestUri = new Uri(url); + var httpRequest = new HttpRequestMessage {Method = HttpMethod.Post, RequestUri = new Uri(url)}; await client.Credentials.ProcessHttpRequestAsync(httpRequest, CancellationToken.None).ConfigureAwait(false); - HttpResponseMessage httpResponse = await client.HttpClient.SendAsync(httpRequest, CancellationToken.None).ConfigureAwait(false); - string responseContent = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - Dictionary result = new Dictionary(); + var httpResponse = await client.HttpClient.SendAsync(httpRequest, CancellationToken.None).ConfigureAwait(false); + var responseContent = await httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); + var result = new Dictionary(); try { - JToken responseDoc = JToken.Parse(responseContent); - string primaryKey = (string)responseDoc["primaryKey"]; - string secondaryKey = (string)responseDoc["secondaryKey"]; + var responseDoc = JToken.Parse(responseContent); + var primaryKey = (string)responseDoc["primaryKey"]; + var secondaryKey = (string)responseDoc["secondaryKey"]; if (string.IsNullOrEmpty(primaryKey) || string.IsNullOrEmpty(secondaryKey)) throw new Exception(); // this is caught by the synced wrapper result.Add(StorageKeyKind.Primary, primaryKey); @@ -136,18 +134,20 @@ public async Task> GetStorageKeysAsync(string private Dictionary GetV2Keys(string resourceGroupName, string storageAccountName) { - Microsoft.Azure.Management.Storage.StorageManagementClient storageClient = GetCurrentStorageV2Client(); + var storageClient = GetCurrentStorageV2Client(); var r = storageClient.StorageAccounts.ListKeys(resourceGroupName, storageAccountName); +// TODO: Remove IfDef #if NETSTANDARD - string k1 = r.Keys[0].Value; - string k2 = r.Keys[1].Value; + var k1 = r.Keys[0].Value; + var k2 = r.Keys[1].Value; #else string k1 = r.StorageAccountKeys.Key1; string k2 = r.StorageAccountKeys.Key2; #endif - Dictionary result = new Dictionary(); - result.Add(StorageKeyKind.Primary, k1); - result.Add(StorageKeyKind.Secondary, k2); + var result = new Dictionary + { + {StorageKeyKind.Primary, k1}, {StorageKeyKind.Secondary, k2} + }; return result; } @@ -158,7 +158,7 @@ public Dictionary GetStorageKeys(string resourceGroupNam { try { - return this.GetStorageKeysAsync(resourceGroupName, storageAccountName).GetAwaiter().GetResult(); + return GetStorageKeysAsync(resourceGroupName, storageAccountName).GetAwaiter().GetResult(); } catch (Exception e) { @@ -171,11 +171,11 @@ public Dictionary GetStorageKeys(string resourceGroupNam /// public string GetStorageResourceGroup(string storageAccountName) { - ResourceManagementClient resourcesClient = GetCurrentResourcesClient(Context); + var resourcesClient = GetCurrentResourcesClient(Context); - foreach (string storageAccountType in new[] { StorageAccountType.ClassicStorage, StorageAccountType.Storage }) + foreach (var storageAccountType in new[] { StorageAccountType.ClassicStorage, StorageAccountType.Storage }) { - string resourceGroup = GetStorageResourceGroup( + var resourceGroup = GetStorageResourceGroup( resourcesClient, storageAccountName, storageAccountType); @@ -189,26 +189,21 @@ public string GetStorageResourceGroup(string storageAccountName) throw new Exception(string.Format(Properties.Resources.StorageAccountNotFound, storageAccountName)); } - private string GetStorageResourceGroup( + private static string GetStorageResourceGroup( ResourceManagementClient resourcesClient, string storageAccountName, string resourceType) { var query = new Rest.Azure.OData.ODataQuery(r => r.ResourceType == resourceType); - Rest.Azure.IPage res = resourcesClient.Resources.List(query); + var res = resourcesClient.Resources.List(query); var allResources = new List(res); - GenericResource account = allResources.Find(r => r.Name == storageAccountName); - if (account != null) - { - string resId = account.Id; - string[] segments = resId.Split('/'); - int indexOfResoureGroup = new List(segments).IndexOf("resourceGroups") + 1; - return segments[indexOfResoureGroup]; - } - else - { - return null; - } + var account = allResources.Find(r => r.Name == storageAccountName); + if (account == null) return null; + + var resId = account.Id; + var segments = resId.Split('/'); + var indexOfResourceGroup = new List(segments).IndexOf("resourceGroups") + 1; + return segments[indexOfResourceGroup]; } public Dictionary GetStorageKeys(string storageName) @@ -220,26 +215,18 @@ public Dictionary GetStorageKeys(string storageName) /// /// Lazy creation of a single instance of a storage client /// - private Microsoft.Azure.Management.Storage.StorageManagementClient GetCurrentStorageV2Client() + private StorageManagementClient GetCurrentStorageV2Client() { - if (StorageV2Client == null) - { - StorageV2Client = GetStorageV2Client(Context); - } - - return StorageV2Client; + return StorageV2Client ?? (StorageV2Client = GetStorageV2Client(Context)); } /// - /// Lazy creation of a single instance of a resoures client + /// Lazy creation of a single instance of a resources client /// private ResourceManagementClient GetCurrentResourcesClient(IAzureContext context) { - if (ResourcesClient == null) - { - ResourcesClient = AzureSession.Instance.ClientFactory.CreateArmClient(Context, AzureEnvironment.Endpoint.ResourceManager); - } - return ResourcesClient; + return ResourcesClient ?? (ResourcesClient = + AzureSession.Instance.ClientFactory.CreateArmClient(Context, AzureEnvironment.Endpoint.ResourceManager)); } /// @@ -252,7 +239,7 @@ internal Dictionary RetrieveStorageKeys(Guid storageAcco { // Retrieve the id of the storage account. // - string storageAccountId = RetrieveStorageAccountIdAsync(storageAccountSubscriptionId, storageAccountName).GetAwaiter().GetResult(); + var storageAccountId = RetrieveStorageAccountIdAsync(storageAccountSubscriptionId, storageAccountName).GetAwaiter().GetResult(); // Extract storage account keys. // @@ -266,28 +253,28 @@ internal Dictionary RetrieveStorageKeys(Guid storageAcco /// Dictionary containing storage keys private async Task> RetrieveStorageKeysAsync(string storageAccountId) { - bool isClassicStorage = storageAccountId.Contains("Microsoft.ClassicStorage/storageAccounts"); + var isClassicStorage = storageAccountId.Contains("Microsoft.ClassicStorage/storageAccounts"); // Build a URI for calling corresponding REST-API // - StringBuilder uriBuilder = new StringBuilder(Context.Environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager).ToString()); + var uriBuilder = new StringBuilder(Context.Environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager).ToString()); uriBuilder.AppendFormat("{0}/listKeys?api-version={1}", storageAccountId, isClassicStorage ? ClassicStorageListKeysApiVersion : NonClassicStorageListKeysApiVersion); // Define an exception to be thrown on failure. // - Exception exception = new Exception(string.Format(Properties.Resources.RetrievingStorageAccountKeysFailed, storageAccountId)); + var exception = new Exception(string.Format(Properties.Resources.RetrievingStorageAccountKeysFailed, storageAccountId)); // Call the URI and get storage account keys. // - JToken storageAccountKeysResponse = await SendAsync(uriBuilder.ToString(), HttpMethod.Post, exception); + var storageAccountKeysResponse = await SendAsync(uriBuilder.ToString(), HttpMethod.Post, exception); // Extract keys out of response. // - Dictionary storageAccountKeys = new Dictionary(); - string primaryKey = null; - string secondaryKey = null; + var storageAccountKeys = new Dictionary(); + string primaryKey; + string secondaryKey; if (isClassicStorage) { primaryKey = (string)storageAccountKeysResponse[PrimaryKey]; @@ -295,7 +282,7 @@ private async Task> RetrieveStorageKeysAsync( } else { - JArray storageAccountKeysArray = (JArray)storageAccountKeysResponse["keys"]; + var storageAccountKeysArray = (JArray)storageAccountKeysResponse["keys"]; if (storageAccountKeysArray == null) { throw exception; @@ -325,11 +312,11 @@ private async Task RetrieveStorageAccountIdAsync(Guid storageAccountSubs { // Build a URI for calling corresponding REST-API. // - StringBuilder uriBuilder = new StringBuilder(Context.Environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager).ToString()); + var uriBuilder = new StringBuilder(Context.Environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager).ToString()); uriBuilder.AppendFormat("/resources?api-version=2018-05-01&$filter=(subscriptionId%20eq%20'{0}')%20and%20((resourceType%20eq%20'microsoft.storage/storageaccounts')%20or%20(resourceType%20eq%20'microsoft.classicstorage/storageaccounts'))%20and%20(name%20eq%20'{1}')", storageAccountSubscriptionId, storageAccountName); - string nextLink = uriBuilder.ToString(); + var nextLink = uriBuilder.ToString(); JToken response = null; while (!string.IsNullOrEmpty(nextLink)) @@ -338,14 +325,14 @@ private async Task RetrieveStorageAccountIdAsync(Guid storageAccountSubs nextLink = (string)response["nextLink"]; } - JArray valuesArray = (JArray)response["value"]; + var valuesArray = (JArray)response["value"]; if (!valuesArray.HasValues) { throw new Exception(string.Format(Properties.Resources.StorageAccountNotFound, storageAccountName)); } - JToken idValueToken = valuesArray[0]; - string id = (string)idValueToken["id"]; + var idValueToken = valuesArray[0]; + var id = (string)idValueToken["id"]; if (string.IsNullOrEmpty(id)) { throw new Exception(string.Format(Properties.Resources.RetrievingStorageAccountIdUnderSubscriptionFailed, storageAccountName, storageAccountSubscriptionId)); @@ -363,12 +350,10 @@ private async Task RetrieveStorageAccountIdAsync(Guid storageAccountSubs /// Response of the request. private async Task SendAsync(string url, HttpMethod method, Exception exceptionToThrowOnFailure) { - ResourceManagementClient client = GetCurrentResourcesClient(Context); - HttpRequestMessage httpRequest = new HttpRequestMessage(); - httpRequest.Method = method; - httpRequest.RequestUri = new Uri(url); + var client = GetCurrentResourcesClient(Context); + var httpRequest = new HttpRequestMessage {Method = method, RequestUri = new Uri(url)}; await client.Credentials.ProcessHttpRequestAsync(httpRequest, CancellationToken.None).ConfigureAwait(false); - HttpResponseMessage httpResponse = await client.HttpClient.SendAsync(httpRequest, CancellationToken.None).ConfigureAwait(false); + var httpResponse = await client.HttpClient.SendAsync(httpRequest, CancellationToken.None).ConfigureAwait(false); if (!httpResponse.IsSuccessStatusCode) { throw exceptionToThrowOnFailure; @@ -378,12 +363,12 @@ private async Task SendAsync(string url, HttpMethod method, Exception ex } /// - /// Verson of classic storage listKeys REST-API. + /// Version of classic storage listKeys REST-API. /// private const string ClassicStorageListKeysApiVersion = "2016-11-01"; /// - /// Verson of non classic storage listKeys REST-API. + /// Version of non classic storage listKeys REST-API. /// private const string NonClassicStorageListKeysApiVersion = "2017-06-01"; diff --git a/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/BaseSqlVulnerabilityAssessmentAdapter.cs b/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/BaseSqlVulnerabilityAssessmentAdapter.cs index 747f4d324677..51b28136064d 100644 --- a/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/BaseSqlVulnerabilityAssessmentAdapter.cs +++ b/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/BaseSqlVulnerabilityAssessmentAdapter.cs @@ -15,10 +15,10 @@ using Microsoft.Azure.Commands.Common.Authentication.Abstractions; using Microsoft.Azure.Commands.Sql.VulnerabilityAssessment.Model; using Microsoft.Azure.Management.Sql.Models; +// TODO: Remove IfDef code #if !NETSTANDARD using Microsoft.WindowsAzure.Storage.Blob; #endif - using System; using System.Linq; @@ -71,7 +71,7 @@ public DatabaseVulnerabilityAssessmentSettingsModel ConvertSettingsToModel(strin if (settings.RecurringScans == null) { // This should never happen - settings.RecurringScans = new VulnerabilityAssessmentRecurringScansProperties() + settings.RecurringScans = new VulnerabilityAssessmentRecurringScansProperties { IsEnabled = false, EmailSubscriptionAdmins = true, @@ -80,17 +80,17 @@ public DatabaseVulnerabilityAssessmentSettingsModel ConvertSettingsToModel(strin } // Getting storage info from StorageContainerPath - string storageAccountName = string.Empty; - string storageBlobContainerName = string.Empty; + var storageAccountName = string.Empty; + var storageBlobContainerName = string.Empty; if (!string.IsNullOrEmpty(settings.StorageContainerPath)) { - string storageAccountNamePart = settings.StorageContainerPath.Split(new string[] { "https://" }, StringSplitOptions.RemoveEmptyEntries)[0]; + var storageAccountNamePart = settings.StorageContainerPath.Split(new[] { "https://" }, StringSplitOptions.RemoveEmptyEntries)[0]; if (!string.IsNullOrEmpty(storageAccountNamePart)) { - storageAccountName = storageAccountNamePart.Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries)[0]; + storageAccountName = storageAccountNamePart.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries)[0]; } - -#if!NETSTANDARD +// TODO: Remove IfDef code +#if !NETSTANDARD CloudBlob cloudBlob = new CloudBlob(new Uri(settings.StorageContainerPath)); storageBlobContainerName = cloudBlob.Container.Name; #endif diff --git a/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/VulnerabilityAssessmentEndpointsCommunicator.cs b/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/VulnerabilityAssessmentEndpointsCommunicator.cs index 23e5530ca109..cd74858af108 100644 --- a/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/VulnerabilityAssessmentEndpointsCommunicator.cs +++ b/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/VulnerabilityAssessmentEndpointsCommunicator.cs @@ -18,6 +18,7 @@ using Microsoft.Azure.Management.Sql; using Microsoft.Azure.Management.Sql.Models; using Microsoft.Azure.Management.Storage; +// TODO: Remove IfDef code #if !NETSTANDARD using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Blob; @@ -26,7 +27,6 @@ using System.Collections.Generic; using System.Linq; - namespace Microsoft.Azure.Commands.Sql.VulnerabilityAssessment.Services { /// @@ -57,11 +57,10 @@ public class VulnerabilityAssessmentEndpointsCommunicator public VulnerabilityAssessmentEndpointsCommunicator(IAzureContext context) { Context = context; - if (context.Subscription != Subscription) - { - Subscription = context.Subscription; - SqlClient = null; - } + if (context.Subscription == Subscription) return; + + Subscription = context.Subscription; + SqlClient = null; } /// @@ -166,10 +165,10 @@ public struct StorageContainerInfo /// public StorageContainerInfo CreateBlobStorageContainer(string resourceGroupName, string storageAccountName, string containerName) { - StorageManagementClient storageClient = GetCurrentStorageClient(); + var storageClient = GetCurrentStorageClient(); var storageAccountObject = storageClient.StorageAccounts.GetProperties(resourceGroupName, storageAccountName); var keysObject = storageClient.StorageAccounts.ListKeys(resourceGroupName, storageAccountName); - +// TODO: Remove IfDef #if NETSTANDARD var storageAccountBlobPrimaryEndpoints = storageAccountObject.PrimaryEndpoints.Blob; var key = keysObject.Keys.FirstOrDefault().Value; @@ -205,8 +204,6 @@ public StorageContainerInfo CreateBlobStorageContainer(string resourceGroupName, StorageAccountSasKey = key, StorageContainerPath = string.Format("{0}{1}", storageAccountBlobPrimaryEndpoints, containerName) }; - - } /// From 9ff30321bd3d22e91a0daa71eb86105240c525ce Mon Sep 17 00:00:00 2001 From: MiYanni Date: Mon, 29 Oct 2018 19:01:34 -0700 Subject: [PATCH 08/15] Updated Websites #if statements. --- .../AppServicePlans/GetAzureAppServicePlan.cs | 13 +-- .../AppServicePlans/NewAzureAppServicePlan.cs | 47 ++++---- .../AppServicePlans/SetAzureAppServicePlan.cs | 5 +- .../Cmdlets/WebApps/GetAzureWebApp.cs | 9 +- .../Models.WebApp/AppServicePlanBaseCmdlet.cs | 4 - .../Utilities/CmdletHelpers.cs | 108 +++++++++--------- .../Utilities/WebsitesClientExtensions.cs | 57 +++++---- .../ValidateServerFarmAttribute.cs | 6 +- 8 files changed, 113 insertions(+), 136 deletions(-) diff --git a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/GetAzureAppServicePlan.cs b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/GetAzureAppServicePlan.cs index 6032786a1227..b790e876b221 100644 --- a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/GetAzureAppServicePlan.cs +++ b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/GetAzureAppServicePlan.cs @@ -24,11 +24,6 @@ using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters; using Microsoft.Azure.Commands.WebApps.Models.WebApp; -#if NETSTANDARD -using ServerFarmWithRichSku = Microsoft.Azure.Management.WebSites.Models.AppServicePlan; -using ServerFarmCollection = System.Collections.Generic.IList; -#endif - namespace Microsoft.Azure.Commands.WebApps.Cmdlets.AppServicePlans { /// @@ -95,7 +90,7 @@ private void GetByAppServicePlanName() WriteProgress(progressRecord); - var serverFarmResources = this.ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions() + var serverFarmResources = ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions { ResourceType = "Microsoft.Web/ServerFarms" }).Where(sf => string.Equals(sf.Name, Name, StringComparison.OrdinalIgnoreCase)).ToArray(); @@ -141,7 +136,7 @@ private void GetBySubscription() WriteProgress(progressRecord); - var resourceGroups = this.ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions() + var resourceGroups = ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions { ResourceType = "Microsoft.Web/ServerFarms" }).Select(sf => sf.ResourceGroupName).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); @@ -154,7 +149,7 @@ private void GetBySubscription() try { var result = WebsitesClient.ListAppServicePlans(rg); - if (result != null && result != null) + if (result != null) { foreach(var item in result) { @@ -183,7 +178,7 @@ private void GetByLocation() WriteProgress(progressRecord); - var serverFarmResources = this.ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions() + var serverFarmResources = ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions { ResourceType = "Microsoft.Web/ServerFarms" }).Where(sf => string.Equals(sf.Location, Location.Replace(" ", string.Empty), StringComparison.OrdinalIgnoreCase)).ToArray(); diff --git a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/NewAzureAppServicePlan.cs b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/NewAzureAppServicePlan.cs index 767aef96d5c8..20d2cc4c4726 100644 --- a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/NewAzureAppServicePlan.cs +++ b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/NewAzureAppServicePlan.cs @@ -20,9 +20,6 @@ using System; using System.Management.Automation; -#if NETSTANDARD -using ServerFarmWithRichSku = Microsoft.Azure.Management.WebSites.Models.AppServicePlan; -#endif namespace Microsoft.Azure.Commands.WebApps.Cmdlets.AppServicePlans { /// @@ -60,24 +57,24 @@ public class NewAzureAppServicePlanCmdlet : AppServicePlanBaseCmdlet [ValidateNotNullOrEmpty] public bool PerSiteScaling { get; set; } - [Parameter(ParameterSetName = ParameterSet1Name, Mandatory = false, HelpMessage = "Specify this, App Service Plan will run Windows Containers")] - public SwitchParameter HyperV { get; set; } + [Parameter(ParameterSetName = ParameterSet1Name, Mandatory = false, HelpMessage = "Specify this, App Service Plan will run Windows Containers")] + public SwitchParameter HyperV { get; set; } - [Parameter(Mandatory = false, HelpMessage = "Run cmdlet in the background")] + [Parameter(Mandatory = false, HelpMessage = "Run cmdlet in the background")] public SwitchParameter AsJob { get; set; } public override void ExecuteCmdlet() { - if (HyperV.IsPresent && Tier != "PremiumContainer") - { - throw new Exception("HyperV switch is only allowed for PremiumContainer tier"); - } - if (!HyperV.IsPresent && Tier == "PremiumContainer") - { - throw new Exception("PremiumContainer tier is only allowed if HyperV switch is present"); - } - - if (string.IsNullOrWhiteSpace(Tier)) + if (HyperV.IsPresent && Tier != "PremiumContainer") + { + throw new Exception("HyperV switch is only allowed for PremiumContainer tier"); + } + if (!HyperV.IsPresent && Tier == "PremiumContainer") + { + throw new Exception("PremiumContainer tier is only allowed if HyperV switch is present"); + } + + if (string.IsNullOrWhiteSpace(Tier)) { Tier = "Free"; } @@ -105,17 +102,17 @@ public override void ExecuteCmdlet() Capacity = capacity }; - var appServicePlan = new AppServicePlan - { - Location = Location, - Sku = sku, - AdminSiteName = null, - PerSiteScaling = PerSiteScaling, - IsXenon = HyperV.IsPresent - }; + var appServicePlan = new AppServicePlan + { + Location = Location, + Sku = sku, + AdminSiteName = null, + PerSiteScaling = PerSiteScaling, + IsXenon = HyperV.IsPresent + }; - WriteObject(new PSAppServicePlan(WebsitesClient.CreateOrUpdateAppServicePlan(ResourceGroupName, Name, appServicePlan, AseName, aseResourceGroupName)), true); + WriteObject(new PSAppServicePlan(WebsitesClient.CreateOrUpdateAppServicePlan(ResourceGroupName, Name, appServicePlan, AseName, aseResourceGroupName)), true); } } } diff --git a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/SetAzureAppServicePlan.cs b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/SetAzureAppServicePlan.cs index e4f0489f697a..58085e561837 100644 --- a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/SetAzureAppServicePlan.cs +++ b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/SetAzureAppServicePlan.cs @@ -19,9 +19,6 @@ using System.Text.RegularExpressions; using Microsoft.Azure.Commands.WebApps.Models.WebApp; -#if NETSTANDARD -using ServerFarmWithRichSku = Microsoft.Azure.Management.WebSites.Models.AppServicePlan; -#endif namespace Microsoft.Azure.Commands.WebApps.Cmdlets.AppServicePlans { /// @@ -61,7 +58,7 @@ public override void ExecuteCmdlet() AppServicePlan = new PSAppServicePlan(WebsitesClient.GetAppServicePlan(ResourceGroupName, Name)); AppServicePlan.Sku.Tier = string.IsNullOrWhiteSpace(Tier) ? AppServicePlan.Sku.Tier : Tier; AppServicePlan.Sku.Capacity = NumberofWorkers > 0 ? NumberofWorkers : AppServicePlan.Sku.Capacity; - int workerSizeAsNumber = 0; + int workerSizeAsNumber; int.TryParse(Regex.Match(AppServicePlan.Sku.Name, @"\d+").Value, out workerSizeAsNumber); AppServicePlan.Sku.Name = string.IsNullOrWhiteSpace(WorkerSize) ? CmdletHelpers.GetSkuName(AppServicePlan.Sku.Tier, workerSizeAsNumber) : CmdletHelpers.GetSkuName(AppServicePlan.Sku.Tier, WorkerSize); AppServicePlan.PerSiteScaling = PerSiteScaling; diff --git a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/WebApps/GetAzureWebApp.cs b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/WebApps/GetAzureWebApp.cs index 5f41b855669f..196aab7ff6e2 100644 --- a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/WebApps/GetAzureWebApp.cs +++ b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/WebApps/GetAzureWebApp.cs @@ -24,9 +24,6 @@ using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters; using Microsoft.Azure.Commands.WebApps.Models.WebApp; -#if NETSTANDARD -using ServerFarmWithRichSku = Microsoft.Azure.Management.WebSites.Models.AppServicePlan; -#endif namespace Microsoft.Azure.Commands.WebApps.Cmdlets.WebApps { /// @@ -96,7 +93,7 @@ private void GetByWebAppName() WriteProgress(progressRecord); - var sites = this.ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions() + var sites = ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions { ResourceType = "Microsoft.Web/Sites" }).Where(s => string.Equals(s.Name, Name, StringComparison.OrdinalIgnoreCase)).ToArray(); @@ -153,7 +150,7 @@ private void GetBySubscription() WriteProgress(progressRecord); - var resourceGroups = this.ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions() + var resourceGroups = ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions { ResourceType = "Microsoft.Web/Sites" }).Select(s => s.ResourceGroupName).Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); @@ -195,7 +192,7 @@ private void GetByLocation() WriteProgress(progressRecord); - var sites = this.ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions() + var sites = ResourcesClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions { ResourceType = "Microsoft.Web/Sites" }).Where(s => string.Equals(s.Location, Location.Replace(" ", string.Empty), StringComparison.OrdinalIgnoreCase)).ToArray(); diff --git a/src/ResourceManager/Websites/Commands.Websites/Models.WebApp/AppServicePlanBaseCmdlet.cs b/src/ResourceManager/Websites/Commands.Websites/Models.WebApp/AppServicePlanBaseCmdlet.cs index eaa6d7e7033c..22f8630640c5 100644 --- a/src/ResourceManager/Websites/Commands.Websites/Models.WebApp/AppServicePlanBaseCmdlet.cs +++ b/src/ResourceManager/Websites/Commands.Websites/Models.WebApp/AppServicePlanBaseCmdlet.cs @@ -20,10 +20,6 @@ using System; using System.Management.Automation; -#if NETSTANDARD -using ServerFarmWithRichSku = Microsoft.Azure.Management.WebSites.Models.AppServicePlan; -#endif - namespace Microsoft.Azure.Commands.WebApps { public abstract class AppServicePlanBaseCmdlet : WebAppBaseClientCmdLet diff --git a/src/ResourceManager/Websites/Commands.Websites/Utilities/CmdletHelpers.cs b/src/ResourceManager/Websites/Commands.Websites/Utilities/CmdletHelpers.cs index 694040a108c0..c09171c60a47 100644 --- a/src/ResourceManager/Websites/Commands.Websites/Utilities/CmdletHelpers.cs +++ b/src/ResourceManager/Websites/Commands.Websites/Utilities/CmdletHelpers.cs @@ -54,43 +54,43 @@ public static class CmdletHelpers private const string ApplicationServiceEnvironmentResourceIdFormat = "/subscriptions/{0}/resourcegroups/{1}/providers/Microsoft.Web/{2}/{3}"; + public const string DocerRegistryServerUrl = "DOCKER_REGISTRY_SERVER_URL"; + public const string DocerRegistryServerUserName = "DOCKER_REGISTRY_SERVER_USERNAME"; + public const string DocerRegistryServerPassword = "DOCKER_REGISTRY_SERVER_PASSWORD"; + public const string DockerEnableCI = "DOCKER_ENABLE_CI"; + public const string DockerImagePrefix = "DOCKER|"; - public const string DocerRegistryServerUrl = "DOCKER_REGISTRY_SERVER_URL"; - public const string DocerRegistryServerUserName = "DOCKER_REGISTRY_SERVER_USERNAME"; - public const string DocerRegistryServerPassword = "DOCKER_REGISTRY_SERVER_PASSWORD"; - public const string DockerEnableCI = "DOCKER_ENABLE_CI"; - public const string DockerImagePrefix = "DOCKER|"; - - public static Dictionary ConvertToStringDictionary(this Hashtable hashtable) + public static Dictionary ConvertToStringDictionary(this Hashtable hashtable) { - return hashtable == null ? null : hashtable.Cast() + return hashtable?.Cast() .ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value.ToString(), StringComparer.Ordinal); } public static Dictionary ConvertToConnectionStringDictionary(this Hashtable hashtable) { - return hashtable == null ? null : hashtable.Cast() + return hashtable?.Cast() .ToDictionary( - kvp => kvp.Key.ToString(), kvp => - { - var typeValuePair = new Hashtable((Hashtable)kvp.Value, StringComparer.OrdinalIgnoreCase); - var type = (ConnectionStringType)Enum.Parse(typeof(ConnectionStringType), typeValuePair["Type"].ToString(), true); - return new ConnStringValueTypePair + kvp => kvp.Key.ToString(), kvp => { - Type = type, - Value = typeValuePair["Value"].ToString() - }; - }); + var typeValuePair = new Hashtable((Hashtable)kvp.Value, StringComparer.OrdinalIgnoreCase); + var type = (ConnectionStringType)Enum.Parse(typeof(ConnectionStringType), typeValuePair["Type"].ToString(), true); + return new ConnStringValueTypePair + { + Type = type, + Value = typeValuePair["Value"].ToString() + }; + }); } internal static bool ShouldUseDeploymentSlot(string webSiteName, string slotName, out string qualifiedSiteName) { - bool result = false; + var result = false; qualifiedSiteName = webSiteName; -#if !NETSTANDARD - var siteNamePattern = "{0}({1})"; +// TODO: Remove IfDef +#if NETSTANDARD + const string siteNamePattern = "{0}/{1}"; #else - var siteNamePattern = "{0}/{1}"; + const string siteNamePattern = "{0}({1})"; #endif if (!string.IsNullOrEmpty(slotName) && !string.Equals(slotName, "Production", StringComparison.OrdinalIgnoreCase)) { @@ -113,7 +113,7 @@ internal static HostingEnvironmentProfile CreateHostingEnvironmentProfile(string internal static string BuildMetricFilter(DateTime? startTime, DateTime? endTime, string timeGrain, IReadOnlyList metricNames) { - var dateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ"; + const string dateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ"; var filter = ""; if (metricNames != null && metricNames.Count > 0) { @@ -205,13 +205,13 @@ internal static string GetSkuName(string tier, int workerSize) sku = "P" + workerSize + "V2"; return sku; } - else if (string.Equals("PremiumContainer", tier, StringComparison.OrdinalIgnoreCase)) - { - sku = "PC" + (workerSize + 1); - return sku; - } - else - { + else if (string.Equals("PremiumContainer", tier, StringComparison.OrdinalIgnoreCase)) + { + sku = "PC" + (workerSize + 1); + return sku; + } + else + { sku = string.Empty + tier[0]; } @@ -222,24 +222,24 @@ internal static string GetSkuName(string tier, int workerSize) internal static string GetSkuName(string tier, string workerSize) { string sku; - if (string.Equals("Shared", tier, StringComparison.OrdinalIgnoreCase)) - { - sku = "D"; - } - else if (string.Equals("PremiumV2", tier, StringComparison.OrdinalIgnoreCase)) - { - sku = "P" + WorkerSizes[workerSize] + "V2"; - return sku; - } - else if (string.Equals("PremiumContainer", tier, StringComparison.OrdinalIgnoreCase)) - { - sku = "PC" + (WorkerSizes[workerSize] + 1); - return sku; - } - else - { - sku = string.Empty + tier[0]; - } + if (string.Equals("Shared", tier, StringComparison.OrdinalIgnoreCase)) + { + sku = "D"; + } + else if (string.Equals("PremiumV2", tier, StringComparison.OrdinalIgnoreCase)) + { + sku = "P" + WorkerSizes[workerSize] + "V2"; + return sku; + } + else if (string.Equals("PremiumContainer", tier, StringComparison.OrdinalIgnoreCase)) + { + sku = "PC" + (WorkerSizes[workerSize] + 1); + return sku; + } + else + { + sku = string.Empty + tier[0]; + } sku += WorkerSizes[workerSize]; return sku; @@ -323,7 +323,7 @@ internal static void ExtractWebAppPropertiesFromWebApp(Site webapp, out string r internal static Certificate[] GetCertificates(ResourceClient resourceClient, WebsitesClient websitesClient, string resourceGroupName, string thumbPrint) { - var certificateResources = resourceClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions() + var certificateResources = resourceClient.ResourceManagementClient.FilterResources(new FilterResourcesOptions { ResourceType = "Microsoft.Web/Certificates" }).ToArray(); @@ -348,7 +348,7 @@ internal static Certificate[] GetCertificates(ResourceClient resourceClient, Web internal static SiteConfigResource ConvertToSiteConfigResource(this SiteConfig config) { - return new SiteConfigResource() + return new SiteConfigResource { AlwaysOn = config.AlwaysOn, ApiDefinition = config.ApiDefinition, @@ -392,13 +392,13 @@ internal static SiteConfigResource ConvertToSiteConfigResource(this SiteConfig c VirtualApplications = config.VirtualApplications, VnetName = config.VnetName, WebSocketsEnabled = config.WebSocketsEnabled, - WindowsFxVersion = config.WindowsFxVersion + WindowsFxVersion = config.WindowsFxVersion }; } internal static SiteConfig ConvertToSiteConfig(this SiteConfigResource config) { - return new SiteConfig() + return new SiteConfig { AlwaysOn = config.AlwaysOn, ApiDefinition = config.ApiDefinition, @@ -442,8 +442,8 @@ internal static SiteConfig ConvertToSiteConfig(this SiteConfigResource config) VirtualApplications = config.VirtualApplications, VnetName = config.VnetName, WebSocketsEnabled = config.WebSocketsEnabled, - WindowsFxVersion = config.WindowsFxVersion, - }; + WindowsFxVersion = config.WindowsFxVersion, + }; } } } diff --git a/src/ResourceManager/Websites/Commands.Websites/Utilities/WebsitesClientExtensions.cs b/src/ResourceManager/Websites/Commands.Websites/Utilities/WebsitesClientExtensions.cs index ffee83da43b0..cddd8889342e 100644 --- a/src/ResourceManager/Websites/Commands.Websites/Utilities/WebsitesClientExtensions.cs +++ b/src/ResourceManager/Websites/Commands.Websites/Utilities/WebsitesClientExtensions.cs @@ -23,33 +23,8 @@ namespace Microsoft.Azure.Management.WebSites { public static class WebsitesClientExtensions { -#if !NETSTANDARD - public static IWebAppsOperations WebApps(this WebSiteManagementClient client) - { - return client.WebApps; - } - - public static IDeletedWebAppsOperations DeletedWebApps(this WebSiteManagementClient client) - { - return client.DeletedWebApps; - } - - public static IAppServicePlansOperations AppServicePlans(this WebSiteManagementClient client) - { - return client.AppServicePlans; - } - - public static string ApiVersion(this WebSiteManagementClient client) - { - return client.ApiVersion(); - } - - public static ICertificatesOperations Certificates(this WebSiteManagementClient client) - { - return client.Certificates; - } - -#else +// TODO: Remove IfDef +#if NETSTANDARD public static void Save(this System.Xml.Linq.XDocument xdoc, string fileName, System.Xml.Linq.SaveOptions options) { using (var fileStream = System.IO.File.Create(fileName)) @@ -507,8 +482,7 @@ public static IEnumerable GetServerFarmMetrics(this IAppServiceP bool temp; if (bool.TryParse(val, out temp)) return temp; - else - return null; + return null; } public static ICertificatesOperations Certificates(this WebSiteManagementClient client) @@ -533,6 +507,31 @@ public static void DeleteCertificate(this ICertificatesOperations certificate, { certificate.Delete(resourceGroupName, name); } +#else + public static IWebAppsOperations WebApps(this WebSiteManagementClient client) + { + return client.WebApps; + } + + public static IDeletedWebAppsOperations DeletedWebApps(this WebSiteManagementClient client) + { + return client.DeletedWebApps; + } + + public static IAppServicePlansOperations AppServicePlans(this WebSiteManagementClient client) + { + return client.AppServicePlans; + } + + public static string ApiVersion(this WebSiteManagementClient client) + { + return client.ApiVersion(); + } + + public static ICertificatesOperations Certificates(this WebSiteManagementClient client) + { + return client.Certificates; + } #endif } } \ No newline at end of file diff --git a/src/ResourceManager/Websites/Commands.Websites/Validations/ValidateServerFarmAttribute.cs b/src/ResourceManager/Websites/Commands.Websites/Validations/ValidateServerFarmAttribute.cs index 594123042559..7c818dcfc9bd 100644 --- a/src/ResourceManager/Websites/Commands.Websites/Validations/ValidateServerFarmAttribute.cs +++ b/src/ResourceManager/Websites/Commands.Websites/Validations/ValidateServerFarmAttribute.cs @@ -17,10 +17,6 @@ using System.Management.Automation; using System.Text.RegularExpressions; -#if NETSTANDARD -using ServerFarmWithRichSku = Microsoft.Azure.Management.WebSites.Models.AppServicePlan; -#endif - namespace Microsoft.Azure.Commands.WebApps.Validations { public class ValidateServerFarmAttribute : ValidateArgumentsAttribute @@ -36,7 +32,7 @@ protected override void Validate(object arguments, EngineIntrinsics engineIntrin ValidateSku(serverFarm.Sku); } - private void ValidateSku(SkuDescription sku) + private static void ValidateSku(SkuDescription sku) { if (sku == null) { From d090e28b6392381c61c2d38daa24d3c7ed972e0b Mon Sep 17 00:00:00 2001 From: MiYanni Date: Tue, 30 Oct 2018 16:35:45 -0700 Subject: [PATCH 09/15] Copied the necessary ServiceManagement Compute files to ResourceManager Compute. Updated Compute #if statements. --- .../Commands.Compute.Netcore.csproj | 79 -- .../Commands.Compute/Commands.Compute.csproj | 316 ++--- .../Extension/DSC/ConfigurationParseResult.cs | 27 + .../DSC/ConfigurationParsingHelper.cs | 318 +++++ .../DSC/DscExtensionCmdletConstants.cs | 24 + .../DSC/DscExtensionPrivateSettings.cs | 36 + .../DSC/DscExtensionPublicSettings.cs | 111 ++ .../DSC/DscExtensionSettingsSerializer.cs | 177 +++ .../Commands.Compute/Sync/ComputeStats.cs | 51 + .../Sync/Download/BlobHandle.cs | 90 ++ .../Commands.Compute/Sync/Download/BlobUri.cs | 570 ++++++++ .../Sync/Download/Downloader.cs | 168 +++ .../Sync/Download/DownloaderParameters.cs | 30 + .../Sync/IO/StreamWithReadProgress.cs | 104 ++ .../Compute/Commands.Compute/Sync/Program.cs | 80 ++ .../Commands.Compute/Sync/ProgressRecord.cs | 25 + .../Commands.Compute/Sync/ProgressStatus.cs | 104 ++ .../Commands.Compute/Sync/ProgressTracker.cs | 117 ++ .../Sync/ServicePointHandler.cs | 59 + .../Sync/Threading/Parallel.cs | 240 ++++ .../Sync/Upload/BlobCreator.cs | 47 + .../Sync/Upload/BlobCreatorBase.cs | 402 ++++++ .../Sync/Upload/BlobSynchronizer.cs | 102 ++ .../Sync/Upload/ComputeStats.cs | 51 + .../Sync/Upload/DataWithRange.cs | 62 + .../Sync/Upload/ExtensionMethods.cs | 244 ++++ .../Sync/Upload/IndexRangeHelper.cs | 60 + .../Sync/Upload/PatchingBlobCreator.cs | 208 +++ .../Sync/Upload/SerializationUtil.cs | 46 + .../Sync/Upload/UploadContext.cs | 66 + .../Sync/Upload/UploadOperationMetaData.cs | 158 +++ .../VhdManagement/Async/AsyncMachine.cs | 1148 +++++++++++++++++ .../VhdManagement/Async/ExceptionExtension.cs | 59 + .../VhdManagement/Model/AttributeHelper.cs | 94 ++ .../VhdManagement/Model/BitMap.cs | 38 + .../VhdManagement/Model/Block.cs | 65 + .../Model/BlockAllocationTable.cs | 61 + .../VhdManagement/Model/DiskGeometry.cs | 122 ++ .../VhdManagement/Model/DiskType.cs | 24 + .../VhdManagement/Model/HostOsType.cs | 22 + .../VhdManagement/Model/IndexRange.cs | 214 +++ .../VhdManagement/Model/IndexRangeComparer.cs | 33 + .../VhdManagement/Model/Model.cs | 162 +++ .../VhdManagement/Model/ParentLocator.cs | 39 + .../Persistence/AbstractDiskBlockFactory.cs | 73 ++ .../Model/Persistence/BitMapFactory.cs | 50 + .../BlockAllocationTableFactory.cs | 68 + .../Model/Persistence/CloudVhdFileCreator.cs | 60 + .../DifferencingDiskBlockFactory.cs | 87 ++ .../Model/Persistence/DiskTypeFactory.cs | 34 + .../Persistence/DynamicDiskBlockFactory.cs | 77 ++ .../Persistence/FixedDiskBlockFactory.cs | 132 ++ .../Model/Persistence/IBlockFactory.cs | 32 + .../Model/Persistence/SectorFactory.cs | 71 + .../Model/Persistence/StreamHelper.cs | 95 ++ .../Model/Persistence/VhdConstants.cs | 28 + .../Model/Persistence/VhdDataReader.cs | 292 +++++ .../Model/Persistence/VhdDataWriter.cs | 112 ++ .../Model/Persistence/VhdFileFactory.cs | 212 +++ .../Model/Persistence/VhdFooterFactory.cs | 532 ++++++++ .../Model/Persistence/VhdFooterSerializer.cs | 87 ++ .../Model/Persistence/VhdHeaderFactory.cs | 413 ++++++ .../Persistence/VhdParentLocatorFactory.cs | 210 +++ .../VhdManagement/Model/PlatformCode.cs | 27 + .../VhdManagement/Model/Sector.cs | 25 + .../VhdManagement/Model/VhdCookie.cs | 79 ++ .../VhdManagement/Model/VhdCookieType.cs | 22 + .../VhdManagement/Model/VhdCreatorVersion.cs | 35 + .../VhdManagement/Model/VhdEntityAttribute.cs | 23 + .../Model/VhdEntityDescriptor.cs | 46 + .../VhdManagement/Model/VhdFeature.cs | 26 + .../VhdManagement/Model/VhdFile.cs | 106 ++ .../Model/VhdFileFormatVersion.cs | 55 + .../VhdManagement/Model/VhdFooter.cs | 141 ++ .../VhdManagement/Model/VhdHeader.cs | 77 ++ .../VhdManagement/Model/VhdHeaderVersion.cs | 38 + .../Model/VhdPropertyAttribute.cs | 25 + .../Model/VhdPropertyDescriptor.cs | 27 + .../VhdManagement/Model/VhdTimeStamp.cs | 47 + .../VhdManagement/SparseStream.cs | 54 + .../VhdManagement/VirtualDiskStream.cs | 257 ++++ 81 files changed, 9712 insertions(+), 316 deletions(-) create mode 100644 src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParseResult.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParsingHelper.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionCmdletConstants.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPrivateSettings.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPublicSettings.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionSettingsSerializer.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/ComputeStats.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Download/BlobHandle.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Download/BlobUri.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Download/Downloader.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Download/DownloaderParameters.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/IO/StreamWithReadProgress.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Program.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/ProgressRecord.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/ProgressStatus.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/ProgressTracker.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/ServicePointHandler.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Threading/Parallel.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobCreator.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobCreatorBase.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobSynchronizer.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/ComputeStats.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/DataWithRange.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/ExtensionMethods.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/IndexRangeHelper.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/PatchingBlobCreator.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/SerializationUtil.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/UploadContext.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/Sync/Upload/UploadOperationMetaData.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Async/AsyncMachine.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Async/ExceptionExtension.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/AttributeHelper.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/BitMap.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Block.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/BlockAllocationTable.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/DiskGeometry.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/DiskType.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/HostOsType.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/IndexRange.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/IndexRangeComparer.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Model.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/ParentLocator.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/AbstractDiskBlockFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/BitMapFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/BlockAllocationTableFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/CloudVhdFileCreator.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DifferencingDiskBlockFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DiskTypeFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DynamicDiskBlockFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/FixedDiskBlockFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/IBlockFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/SectorFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/StreamHelper.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdConstants.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdDataReader.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdDataWriter.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFileFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFooterFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFooterSerializer.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdHeaderFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdParentLocatorFactory.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/PlatformCode.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Sector.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCookie.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCookieType.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCreatorVersion.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdEntityAttribute.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdEntityDescriptor.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFeature.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFile.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFileFormatVersion.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFooter.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdHeader.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdHeaderVersion.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdPropertyAttribute.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdPropertyDescriptor.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdTimeStamp.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/SparseStream.cs create mode 100644 src/ResourceManager/Compute/Commands.Compute/VhdManagement/VirtualDiskStream.cs diff --git a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj index 5fdfd2e052f6..8f854f109bff 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj +++ b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.Netcore.csproj @@ -93,85 +93,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj index f639d9241501..4a81b4780768 100644 --- a/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj +++ b/src/ResourceManager/Compute/Commands.Compute/Commands.Compute.csproj @@ -317,243 +317,85 @@ Common\AzureStorageContext.cs - - Extension\DSC\DscExtensionCmdletConstants.cs - - - Extension\DSC\DscExtensionPrivateSettings.cs - - - Extension\DSC\DscExtensionPublicSettings.cs - - - Extension\DSC\DscExtensionSettingsSerializer.cs - - - Extension\DSC\ConfigurationParseResult.cs - - - Extension\DSC\ConfigurationParsingHelper.cs - - - Sync\ComputeStats.cs - - - Sync\Download\BlobHandle.cs - - - Sync\Download\BlobUri.cs - - - Sync\Download\Downloader.cs - - - Sync\Download\DownloaderParameters.cs - - - Sync\IO\StreamWithReadProgress.cs - - - Sync\Program.cs - - - Sync\ProgressRecord.cs - - - Sync\ProgressStatus.cs - - - Sync\ProgressTracker.cs - - - Sync\ServicePointHandler.cs - - - Sync\Threading\Parallel.cs - - - Sync\Upload\BlobCreator.cs - - - Sync\Upload\BlobCreatorBase.cs - - - Sync\Upload\BlobSynchronizer.cs - - - Sync\Upload\ComputeStats.cs - - - Sync\Upload\DataWithRange.cs - - - Sync\Upload\ExtensionMethods.cs - - - Sync\Upload\IndexRangeHelper.cs - - - Sync\Upload\PatchingBlobCreator.cs - - - Sync\Upload\SerializationUtil.cs - - - Sync\Upload\UploadContext.cs - - - Sync\Upload\UploadOperationMetaData.cs - - - VhdManagement\Async\AsyncMachine.cs - - - VhdManagement\Async\ExceptionExtension.cs - - - VhdManagement\Model\AttributeHelper.cs - - - VhdManagement\Model\BitMap.cs - - - VhdManagement\Model\Block.cs - - - VhdManagement\Model\BlockAllocationTable.cs - - - VhdManagement\Model\DiskGeometry.cs - - - VhdManagement\Model\DiskType.cs - - - VhdManagement\Model\HostOsType.cs - - - VhdManagement\Model\IndexRange.cs - - - VhdManagement\Model\IndexRangeComparer.cs - - - VhdManagement\Model\Model.cs - - - VhdManagement\Model\ParentLocator.cs - - - VhdManagement\Model\Persistance\AbstractDiskBlockFactory.cs - - - VhdManagement\Model\Persistance\BitMapFactory.cs - - - VhdManagement\Model\Persistance\BlockAllocationTableFactory.cs - - - VhdManagement\Model\Persistance\CloudVhdFileCreator.cs - - - VhdManagement\Model\Persistance\DifferencingDiskBlockFactory.cs - - - VhdManagement\Model\Persistance\DiskTypeFactory.cs - - - VhdManagement\Model\Persistance\DynamicDiskBlockFactory.cs - - - VhdManagement\Model\Persistance\FixedDiskBlockFactory.cs - - - VhdManagement\Model\Persistance\IBlockFactory.cs - - - VhdManagement\Model\Persistance\SectorFactory.cs - - - VhdManagement\Model\Persistance\StreamHelper.cs - - - VhdManagement\Model\Persistance\VhdConstants.cs - - - VhdManagement\Model\Persistance\VhdDataReader.cs - - - VhdManagement\Model\Persistance\VhdDataWriter.cs - - - VhdManagement\Model\Persistance\VhdFileFactory.cs - - - VhdManagement\Model\Persistance\VhdFooterFactory.cs - - - VhdManagement\Model\Persistance\VhdFooterSerializer.cs - - - VhdManagement\Model\Persistance\VhdHeaderFactory.cs - - - VhdManagement\Model\Persistance\VhdParentLocatorFactory.cs - - - VhdManagement\Model\PlatformCode.cs - - - VhdManagement\Model\Sector.cs - - - VhdManagement\Model\VhdCookie.cs - - - VhdManagement\Model\VhdCookieType.cs - - - VhdManagement\Model\VhdCreatorVersion.cs - - - VhdManagement\Model\VhdEntityAttribute.cs - - - VhdManagement\Model\VhdEntityDescriptor.cs - - - VhdManagement\Model\VhdFeature.cs - - - VhdManagement\Model\VhdFile.cs - - - VhdManagement\Model\VhdFileFormatVersion.cs - - - VhdManagement\Model\VhdFooter.cs - - - VhdManagement\Model\VhdHeader.cs - - - VhdManagement\Model\VhdHeaderVersion.cs - - - VhdManagement\Model\VhdPropertyAttribute.cs - - - VhdManagement\Model\VhdPropertyDescriptor.cs - - - VhdManagement\Model\VhdTimeStamp.cs - - - VhdManagement\SparseStream.cs - - - VhdManagement\VirtualDiskStream.cs - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParseResult.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParseResult.cs new file mode 100644 index 000000000000..15a9bc914bcd --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParseResult.cs @@ -0,0 +1,27 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Management.Automation.Language; + +namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Publish +{ + public class ConfigurationParseResult + { + public string Path { get; set; } + public ParseError[] Errors { get; set; } + public Dictionary RequiredModules { get; set; } + + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParsingHelper.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParsingHelper.cs new file mode 100644 index 000000000000..32c23e113ae2 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/ConfigurationParsingHelper.cs @@ -0,0 +1,318 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Exceptions; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Language; +using System.Management.Automation.Runspaces; + +namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC.Publish +{ + + public static class ConfigurationParsingHelper + { + private static readonly ConcurrentDictionary _resourceName2ModuleNameCache = + new ConcurrentDictionary(); + + private static bool IsParameterName(CommandElementAst ast, string name) + { + var constantAst = ast as CommandParameterAst; + if (constantAst == null) + { + return false; + } + return String.Equals(constantAst.ParameterName, name, StringComparison.OrdinalIgnoreCase); + } + + private static IEnumerable GetLegacyTopLevelParametersFromAst(CommandAst ast, string parameterName) + { + var parameters = new List(); + IEnumerable commandElement = + ast.CommandElements.Where(x => IsParameterName(x, parameterName)).OfType(); + foreach (var commandElementAst in commandElement) + { + var arrayLiteralAst = commandElementAst.Argument as ArrayLiteralAst; + if (arrayLiteralAst != null) + { + parameters.AddRange(arrayLiteralAst.Elements.OfType().Select(x => x.Value)); + } + } + return parameters; + } + + + private static bool IsCandidateForImportDscResourceAst(Ast ast, int startOffset) + { + return ast.Extent.StartOffset == startOffset && !(ast is StatementBlockAst) && !(ast is NamedBlockAst); + } + private static Dictionary GetSingleAstRequiredModules(Ast ast, IEnumerable tokens, Dictionary modules) + { + var resources = new List(); + var imports = tokens.Where(token => + String.Compare(token.Text, "Import-DscResource", StringComparison.OrdinalIgnoreCase) == 0); + + // + // Create a function with the same name as Import-DscResource keyword and use powershell + // argument function binding to emulate Import-DscResource argument binding. + // + InitialSessionState initialSessionState = InitialSessionState.Create(); + var importDscResourcefunctionEntry = new SessionStateFunctionEntry( + "Import-DscResource", @"param($Name, $ModuleName, $ModuleVersion, $Module) + if ($ModuleName) + { + foreach ($module in $ModuleName) { + if($module.GetType().FullName -eq 'System.Collections.Hashtable'){ + $mVersion = "" + $mName = "" + foreach($modulekey in $module.Keys){ + if($modulekey -eq 'ModuleName'){ + $mName = $module[$modulekey] + } + elseif($modulekey -eq 'ModuleVersion' -or $modulekey -eq 'RequiredVersion'){ + $mVersion = $module[$modulekey] + } + } + + if(!$global:modules.ContainsKey($mName)){ + $global:modules.Add($mName,$mVersion) + } + } + else{ + if(!$global:modules.ContainsKey($module)){ + if($ModuleVersion) + { + $global:modules.Add($module,$ModuleVersion) + } + else + { + $global:modules.Add($module,"""") + } + } + } + } + } + elseif($Module) + { + foreach ($module in $Module) + { + if(!$global:modules.ContainsKey($module)) + { + if($ModuleVersion) + { + $global:modules.Add($module,$ModuleVersion) + } + else + { + $global:modules.Add($module,"""") + } + } + } + } + else + { + foreach ($n in $Name) { $global:resources.Add($n) } + } + "); + + initialSessionState.Commands.Add(importDscResourcefunctionEntry); + initialSessionState.LanguageMode = PSLanguageMode.RestrictedLanguage; + var moduleVarEntry = new SessionStateVariableEntry("modules", modules, ""); + var resourcesVarEntry = new SessionStateVariableEntry("resources", resources, ""); + initialSessionState.Variables.Add(moduleVarEntry); + initialSessionState.Variables.Add(resourcesVarEntry); + + using (System.Management.Automation.PowerShell powerShell = System.Management.Automation.PowerShell.Create(initialSessionState)) + { + foreach (var import in imports) + { + int startOffset = import.Extent.StartOffset; + var asts = ast.FindAll(a => IsCandidateForImportDscResourceAst(a, startOffset), true); + int longestLen = -1; + Ast longestCandidate = null; + foreach (var candidatAst in asts) + { + int curLen = candidatAst.Extent.EndOffset - candidatAst.Extent.StartOffset; + if (curLen > longestLen) + { + longestCandidate = candidatAst; + longestLen = curLen; + } + } + // longestCandidate should contain AST for import-dscresource, like "Import-DSCResource -Module x -Name y". + if (longestCandidate != null) + { + string importText = longestCandidate.Extent.Text; + // We invoke-command "importText" here. Script injection is prevented: + // We checked that file represents a valid AST without errors. + powerShell.AddScript(importText); + powerShell.Invoke(); + powerShell.Commands.Clear(); + } + } + } + + var modulesFromDscResource = resources.Select(GetModuleNameForDscResource); + foreach (var moduleName in modulesFromDscResource) + { + if (!modules.ContainsKey(moduleName)) + { + modules.Add(moduleName, ""); + } + } + + return modules; + } + + public static string GetModuleNameForDscResource(string resourceName) + { + string moduleName; + if (!_resourceName2ModuleNameCache.TryGetValue(resourceName, out moduleName)) + { + try + { + using (System.Management.Automation.PowerShell powershell = System.Management.Automation.PowerShell.Create()) + { + powershell.AddCommand("Get-DscResource"). + AddCommand("Where-Object").AddParameter("Property", "ResourceType").AddParameter("Value", resourceName).AddParameter("EQ", true). + AddCommand("Foreach-Object").AddParameter("MemberName", "Module"). + AddCommand("Foreach-Object").AddParameter("MemberName", "Name"); + moduleName = powershell.Invoke().First(); + } + } + catch (InvalidOperationException e) + { + throw new GetDscResourceException(resourceName, e); + } + _resourceName2ModuleNameCache.TryAdd(resourceName, moduleName); + } + return moduleName; + } + + private static Dictionary GetRequiredModulesFromAst(Ast ast, IEnumerable tokens) + { + var modules = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // We use System.Management.Automation.Language.Parser to extract required modules from ast, + // but format of ast is a bit tricky and have changed in time. + // + // There are two place where 'Import-DscResource' keyword can appear: + // 1) + // Configuration Foo { + // Import-DscResource .... # outside node + // Node Bar {...} + // } + // 2) + // Configuration Foo { + // Node Bar { + // Import-DscResource .... # inside node + // ... + // } + // } + // + // The old version of System.Management.Automation.Language.Parser produces slightly different AST for the first case. + // In new version, Configuration corresponds to ConfigurationDefinitionAst. + // In old version is's a generic CommandAst with specific commandElements which capture top-level Imports (case 1). + // In new version all imports correspond to their own CommandAsts, same for case 2 in old version. + + // Old version, case 1: + IEnumerable legacyConfigurationAsts = ast.FindAll(IsLegacyAstConfiguration, true).Select(x => (CommandAst)x); + foreach (var legacyConfigurationAst in legacyConfigurationAsts) + { + // Note: these two sequences are translated to same AST: + // + // Import-DscResource -Module xComputerManagement; Import-DscResource -Name xComputer + // Import-DscResource -Module xComputerManagement -Name xComputer + // + // We cannot distinguish different imports => cannot ignore resource names for imports with specified modules. + // So we process everything: ModuleDefinition and ResourceDefinition. + + // Example: Import-DscResource -Module xPSDesiredStateConfiguration + + var moduleParams = GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ModuleDefinition"); + foreach (var param in moduleParams) + { + if (!modules.ContainsKey(param)) + { + modules.Add(param, ""); + } + } + + // Example: Import-DscResource -Name MSFT_xComputer + var resourceParams = GetLegacyTopLevelParametersFromAst(legacyConfigurationAst, "ResourceDefinition").Select(GetModuleNameForDscResource); + foreach (var param in resourceParams) + { + if (!modules.ContainsKey(param)) + { + modules.Add(param, ""); + } + } + } + + // Both cases in new version and 2nd case in old version: + modules = GetSingleAstRequiredModules(ast, tokens, modules); + + return modules; + } + + private static bool IsLegacyAstConfiguration(Ast node) + { + var commandNode = node as CommandAst; + if (commandNode == null) + { + return false; + } + // TODO: Add case when configuration name is not a StringConstant, but a variable. + var commandParameter = (commandNode.CommandElements[0] as StringConstantExpressionAst); + if (commandParameter == null) + { + return false; + } + // Find the AST nodes defining configurations. These nodes will be CommandAst nodes + // with 7 or 8 command elements (8 if the configuration requires any custom modules.) + return + commandNode.CommandElements.Count >= 7 && + String.Equals(commandParameter.Extent.Text, "configuration", StringComparison.OrdinalIgnoreCase) && + String.Equals(commandParameter.Value, @"PSDesiredStateConfiguration\Configuration", + StringComparison.OrdinalIgnoreCase); + } + + public static ConfigurationParseResult ParseConfiguration(string path) + { + // Get the resolved script path. This will throw an exception if the file is not found. + string fullPath = Path.GetFullPath(path); + Token[] tokens; + ParseError[] errors; + // Parse the script into an AST, capturing parse errors. Note - even with errors, the + // file may still successfully define one or more configurations. We don't process + // required modules in case of parsing errors to avoid script injection. + ScriptBlockAst ast = Parser.ParseFile(fullPath, out tokens, out errors); + var requiredModules = new Dictionary(); + if (!errors.Any()) + { + requiredModules = GetRequiredModulesFromAst(ast, tokens); + } + return new ConfigurationParseResult() + { + Path = fullPath, + Errors = errors, + RequiredModules = requiredModules, + }; + } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionCmdletConstants.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionCmdletConstants.cs new file mode 100644 index 000000000000..1530c0cbebc9 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionCmdletConstants.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC +{ + public static class DscExtensionCmdletConstants + { + //common extension constants + internal static readonly string ExtensionPublishedNamespace = "Microsoft.Powershell"; + internal static readonly string ExtensionPublishedName = "DSC"; + internal const string DefaultContainerName = "windows-powershell-dsc"; + + internal const int MinMajorPowerShellVersion = 4; + internal const string ZipFileExtension = ".zip"; + internal const string Ps1FileExtension = ".ps1"; + internal const string Psm1FileExtension = ".psm1"; + + + internal static readonly HashSet UploadArchiveAllowedFileExtensions = + new HashSet(StringComparer.OrdinalIgnoreCase) { Ps1FileExtension, Psm1FileExtension, ZipFileExtension }; + internal static readonly HashSet CreateArchiveAllowedFileExtensions = + new HashSet(StringComparer.OrdinalIgnoreCase) { Ps1FileExtension, Psm1FileExtension }; + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPrivateSettings.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPrivateSettings.cs new file mode 100644 index 000000000000..d009de5e1e53 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPrivateSettings.cs @@ -0,0 +1,36 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC +{ + using System.Collections; + + /// + /// Represents private/protected settings. Serialized representation of this object stored as an encrypted string on the VM. + /// Part of the protocol between Set-AzureVMDscExtension cmdlet and DSC Extension handler. + /// + public class DscExtensionPrivateSettings + { + /// + /// Url to the blob storage with ConfigurationData .psd1 file. + /// + public string DataBlobUri { get; set; } + + /// + /// This hashtable contains parameters that needs to be encrypted on target VM, like PSCredential. + /// are not encrypted on target VM. + /// + public Hashtable Items { get; set; } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPublicSettings.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPublicSettings.cs new file mode 100644 index 000000000000..9403fe20b64f --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionPublicSettings.cs @@ -0,0 +1,111 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC +{ + /// + /// Represents public settings. Serialized representation of this object stored as a plain text string on the VM. + /// Part of the protocol between Set-AzureVMDscExtension cmdlet and DSC Extension handler. + /// + public class DscExtensionPublicSettings + { + /// + /// Version 1.0.0.0 of DscExtensionPublicSettings. We keep it for backward compatability. + /// + internal class Version1 + { + public string SasToken { get; set; } + public string ModulesUrl { get; set; } + public string ConfigurationFunction { get; set; } + public Hashtable Properties { get; set; } + + /// + /// Converting to the current version of DscExtensionPublicSettings. + /// + /// + public DscExtensionPublicSettings ToCurrentVersion() + { + var properties = new List(); + foreach (DictionaryEntry p in Properties) + { + properties.Add(new Property + { + Name = p.Key.ToString(), + TypeName = p.Value.GetType().ToString(), + Value = p.Value + }); + } + return new DscExtensionPublicSettings + { + SasToken = SasToken, + ModulesUrl = ModulesUrl, + ConfigurationFunction = ConfigurationFunction, + Properties = properties.ToArray(), + ProtocolVersion = new Version(1, 0, 0, 0) + }; + } + } + + /// + /// Defines an entry of DscExtensionPublicSettings.Properties array. + /// + public class Property + { + public string TypeName { get; set; } + public string Name { get; set; } + public object Value { get; set; } + } + + /// + /// SharedAccessSignature token that allows access of azure blob storage files. + /// + public string SasToken { get; set; } + + /// + /// Url for archive with modules and configuration in azure blob storage. + /// + public string ModulesUrl { get; set; } + + /// + /// String to define configuration in the format: "Module\NameOfConfiguration", where + /// Module can be a path to the root of the module or .ps1 file or .psm1 file. + /// + public string ConfigurationFunction { get; set; } + + /// + /// Configuration parameters + /// + public Property[] Properties { get; set; } + + /// + /// Privacy parameters + /// + public Hashtable Privacy { get; set; } + + /// + /// Version of the protocol (DscExtensionPublicSettings and DscExtensionPrivateSettings mostly). + /// + public Version ProtocolVersion { get; set; } + + /// + /// Specifies the version of the Windows Management Framework (WMF) to install + /// on the VM. + /// + public string WmfVersion { get; set; } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionSettingsSerializer.cs b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionSettingsSerializer.cs new file mode 100644 index 000000000000..628aa46d0bd2 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Extension/DSC/DscExtensionSettingsSerializer.cs @@ -0,0 +1,177 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Newtonsoft.Json; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Management.Automation; +using System.Runtime.InteropServices; +using System.Security; + +namespace Microsoft.WindowsAzure.Commands.Common.Extensions.DSC +{ + public class DscExtensionSettingsSerializer + { + /// + /// Serialize DscExtensionPublicSettings to string. + /// + /// + /// + public static string SerializePublicSettings(DscExtensionPublicSettings extensionPublicSettings) + { + return JsonConvert.SerializeObject(extensionPublicSettings); + } + + /// + /// Serialize DscPrivateSettings to string. + /// + /// + /// + public static string SerializePrivateSettings(DscExtensionPrivateSettings privateSettings) + { + return JsonConvert.SerializeObject(privateSettings); + } + + public static DscExtensionPublicSettings DeserializePublicSettings(string publicSettingsString) + { + DscExtensionPublicSettings extensionPublicSettings; + try + { + extensionPublicSettings = string.IsNullOrEmpty(publicSettingsString) + ? null + : JsonConvert.DeserializeObject(publicSettingsString); + } + catch (JsonException) + { + // Try deserialize as version 1.0 + try + { + DscExtensionPublicSettings.Version1 publicSettingsV1 = + JsonConvert.DeserializeObject(publicSettingsString); + extensionPublicSettings = publicSettingsV1.ToCurrentVersion(); + } + catch (JsonException) + { + throw; + } + } + return extensionPublicSettings; + } + + /// + /// Convert hashtable of public settings into two parts: + /// 1) Array of public settings in format: + /// [ + /// { + /// "Name": "String Parameter", + /// "Value": "String Value", + /// "TypeName": "System.String" + /// } + /// ] + /// 2) Private settings hashtable. We extract all sensitive information (like password from PSCredential) + /// and store it in private settings. Public settings will reference them in form: + /// { + /// "Name": "AdminCredential", + /// "Value": + /// { + /// "Password" : "PrivateSettings:28AC4D36-A99B-41DE-8421-2BCC1C7C1A3B" + /// "UserName" : "DOMAIN\LOGIN" + /// }, + /// "TypeName": "System.Management.Automation.PSCredential" + /// } + /// and private hashtable will look like that: + /// { + /// "28AC4D36-A99B-41DE-8421-2BCC1C7C1A3B" : "password" + /// } + /// + /// + /// tuple of array (public settings) and hashtable (private settings) + public static Tuple SeparatePrivateItems(Hashtable arguments) + { + var publicSettings = new List(); + var privateSettings = new Hashtable(); + if (arguments != null) + { + foreach (DictionaryEntry argument in arguments) + { + object entryValue = argument.Value; + string entryType = argument.Value == null ? "null" : argument.Value.GetType().ToString(); + string entryName = argument.Key.ToString(); + // Special case for PSCredential + PSCredential credential = argument.Value as PSCredential; + if (credential == null) + { + PSObject psObject = argument.Value as PSObject; + if (psObject != null) + { + credential = psObject.BaseObject as PSCredential; + } + } + if (credential != null) + { + // plainTextPassword is a string object with sensitive information in plain text. + // We pass it to 3rd party serializer which may create copies of the string. + string plainTextPassword = ConvertToUnsecureString(credential.Password); + string userName = credential.UserName; + string passwordRef = Guid.NewGuid().ToString(); + privateSettings.Add(passwordRef, plainTextPassword); + var newValue = new Hashtable(); + newValue["UserName"] = String.Format(CultureInfo.InvariantCulture, userName); + newValue["Password"] = String.Format(CultureInfo.InvariantCulture, "PrivateSettingsRef:{0}", + passwordRef); + entryValue = newValue; + entryType = typeof(PSCredential).ToString(); + } + + var entry = new DscExtensionPublicSettings.Property + { + Name = entryName, + TypeName = entryType, + Value = entryValue, + }; + publicSettings.Add(entry); + } + } + return new Tuple(publicSettings.ToArray(), privateSettings); + } + + /// + /// Converte SecureString to String. + /// + /// + /// This method creates a managed object with sensitive information and undetermined lifecycle. + /// + /// + /// + private static string ConvertToUnsecureString(SecureString source) + { + if (source == null) + throw new ArgumentNullException("source"); + + IntPtr unmanagedString = IntPtr.Zero; + try + { + unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(source); + return Marshal.PtrToStringUni(unmanagedString); + } + finally + { + Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString); + } + } + + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/ComputeStats.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/ComputeStats.cs new file mode 100644 index 000000000000..4e5d645a9e64 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/ComputeStats.cs @@ -0,0 +1,51 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace Microsoft.WindowsAzure.Commands.Sync +{ + public class ComputeStats + { + IList history; + int historySize; + + public ComputeStats() : this(60) + { + } + + public ComputeStats(int historySize) + { + history = new List(historySize); + this.historySize = historySize; + } + + public double ComputeAvg(double current) + { + if (history.Count > historySize) + { + history.RemoveAt(0); + } + history.Add(current); + double sum = 0.0; + foreach (var x in history) + { + sum += x; + } + return sum / history.Count; + } + } +} + + diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Download/BlobHandle.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Download/BlobHandle.cs new file mode 100644 index 000000000000..9713fcfbb7f7 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Download/BlobHandle.cs @@ -0,0 +1,90 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Sync.Upload; +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using Microsoft.WindowsAzure.Storage; +using Microsoft.WindowsAzure.Storage.Auth; +using Microsoft.WindowsAzure.Storage.Blob; +using Microsoft.WindowsAzure.Storage.RetryPolicies; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Microsoft.WindowsAzure.Commands.Sync.Download +{ + public class BlobHandle + { + const int MegaByte = 1024 * 1024; + + private readonly BlobUri blobUri; + private readonly string storageAccountKey; + private readonly CloudBlobContainer container; + private readonly BlobRequestOptions blobRequestOptions; + private readonly CloudPageBlob pageBlob; + + public BlobHandle(BlobUri blobUri, string storageAccountKey) + { + this.blobUri = blobUri; + this.storageAccountKey = storageAccountKey; + var blobClient = new CloudBlobClient(new Uri(this.blobUri.BaseUri), new StorageCredentials(this.blobUri.StorageAccountName, this.storageAccountKey)); + this.container = blobClient.GetContainerReference(this.blobUri.BlobContainerName); + this.container.FetchAttributesAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + this.pageBlob = this.container.GetPageBlobReference(blobUri.BlobName); + this.blobRequestOptions = new BlobRequestOptions + { + ServerTimeout = TimeSpan.FromMinutes(5), + RetryPolicy = new LinearRetry(TimeSpan.FromMinutes(1), 3) + }; + this.pageBlob.FetchAttributesAsync(new AccessCondition(), blobRequestOptions, operationContext: null).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + public CloudPageBlob Blob { get { return this.pageBlob; } } + + public IEnumerable GetEmptyRanges() + { + var blobRange = new List { IndexRange.FromLength(0, this.Length) }; + return IndexRange.SubstractRanges(blobRange, GetPageRanges()); + } + + public IEnumerable GetUploadableRanges() + { + IEnumerable ranges = GetPageRanges(); + ranges = Enumerable.ToList(IndexRangeHelper.ChunkRangesBySize(ranges, 2 * MegaByte)); + return ranges; + } + + private IEnumerable GetPageRanges() + { + pageBlob.FetchAttributesAsync(new AccessCondition(), blobRequestOptions, operationContext: null) + .ConfigureAwait(false).GetAwaiter().GetResult(); + IEnumerable pageRanges = pageBlob.GetPageRangesAsync(null, null, new AccessCondition(), blobRequestOptions, operationContext: null) + .ConfigureAwait(false).GetAwaiter().GetResult(); + pageRanges = pageRanges.OrderBy(range => range.StartOffset); + return pageRanges.Select(pr => new IndexRange(pr.StartOffset, pr.EndOffset)); + } + + public Stream OpenStream() + { + return this.container.GetPageBlobReference(blobUri.BlobName).OpenReadAsync(new AccessCondition(), blobRequestOptions, operationContext: null) + .ConfigureAwait(false).GetAwaiter().GetResult(); + } + + public long Length + { + get { return pageBlob.Properties.Length; } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Download/BlobUri.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Download/BlobUri.cs new file mode 100644 index 000000000000..9727a8e233bd --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Download/BlobUri.cs @@ -0,0 +1,570 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Runtime.Serialization; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Sync.Download +{ + public class BlobUri + { + public static bool TryParseUri(Uri uri, out BlobUri blobUri) + { + blobUri = null; + string storageAccountName; + string storageDomainName; + string blobContainerName; + string blobName; + string queryString; + string secret; + + var result = TryParseUri(uri, + out storageAccountName, + out storageDomainName, + out blobContainerName, + out blobName, + out queryString, + out secret); + + if (!result) + { + return false; + } + blobUri = new BlobUri(uri, storageAccountName, storageDomainName, blobContainerName, blobName, queryString); + return true; + } + + internal static bool TryParseUri(Uri blobUri, + out string storageAccountName, + out string storageDomainName, + out string blobContainerName, + out string blobName, + out string queryString, + out string secret) + { + storageAccountName = null; + storageDomainName = null; + blobContainerName = null; + blobName = null; + secret = null; + queryString = null; + + string[] hostSegments = blobUri.DnsSafeHost.ToLower().Split('.'); + + if (hostSegments.Length < 2) + { + return false; + } + + storageAccountName = hostSegments[0]; + storageDomainName = string.Join(".", hostSegments, 1, hostSegments.Length - 1); + + blobContainerName = null; + blobName = null; + + string[] segments = blobUri.AbsolutePath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + + if (segments.Length < 2) + { + return false; //Must have atleast a containerName and BlobName + } + + blobName = HttpUtility.UrlDecode( + string.Join( + "/", segments, 1, segments.Length - 1)); + blobContainerName = HttpUtility.UrlDecode(segments[0]); + + NameValueCollection queryValues = HttpUtility.ParseQueryString(blobUri.Query); + + StringBuilder queryBuilder = new StringBuilder(); + bool firstQuery = true; + + foreach (string key in queryValues.AllKeys) + { + if (string.Equals(key, "dsas_secret", StringComparison.OrdinalIgnoreCase)) + { + secret = queryValues["dsas_secret"]; + } + else + { + if (firstQuery) + { + queryBuilder.AppendFormat("?{0}={1}", key.Replace("?", ""), queryValues[key]); + firstQuery = false; + } + else + { + queryBuilder.AppendFormat("&{0}={1}", key, queryValues[key]); + } + } + } + + if (!string.IsNullOrEmpty(secret)) + { + //TODO[JWA], Find out the complete set of decoding; + secret = secret.Replace(' ', '+'); + } + + queryString = queryBuilder.ToString(); + return true; + } + + public Uri Uri { get; private set; } + public string BaseUri { get; private set; } + public string StorageAccountName { get; private set; } + public string StorageDomainName { get; private set; } + public string BlobContainerName { get; private set; } + public string BlobName { get; private set; } + public string QueryString { get; set; } + public string BlobPath { get; private set; } + + public BlobUri(Uri uri, string storageAccountName, string storageDomainName, string blobContainerName, string blobName, string queryString) + { + Uri = uri; + StorageAccountName = storageAccountName; + StorageDomainName = storageDomainName; + BlobContainerName = blobContainerName; + BlobName = blobName; + QueryString = queryString; + BaseUri = uri.Scheme + Uri.SchemeDelimiter + uri.DnsSafeHost; + BlobPath = this.BaseUri + uri.LocalPath; + } + + } + + #region HttpUtility port from .Net 4.0 + + class HttpUtility + { + public static string UrlDecode(string str) + { + if (str == null) + { + return null; + } + return UrlDecode(str, Encoding.UTF8); + } + + public static string UrlDecode(string str, Encoding e) + { + if (str == null) + { + return null; + } + return UrlDecodeStringFromStringInternal(str, e); + } + + private static string UrlDecodeStringFromStringInternal(string s, Encoding e) + { + int length = s.Length; + UrlDecoder decoder = new UrlDecoder(length, e); + for (int i = 0; i < length; i++) + { + char ch = s[i]; + if (ch == '+') + { + ch = ' '; + } + else if ((ch == '%') && (i < (length - 2))) + { + if ((s[i + 1] == 'u') && (i < (length - 5))) + { + int num3 = HexToInt(s[i + 2]); + int num4 = HexToInt(s[i + 3]); + int num5 = HexToInt(s[i + 4]); + int num6 = HexToInt(s[i + 5]); + if (((num3 < 0) || (num4 < 0)) || ((num5 < 0) || (num6 < 0))) + { + goto Label_0106; + } + ch = (char)((((num3 << 12) | (num4 << 8)) | (num5 << 4)) | num6); + i += 5; + decoder.AddChar(ch); + continue; + } + int num7 = HexToInt(s[i + 1]); + int num8 = HexToInt(s[i + 2]); + if ((num7 >= 0) && (num8 >= 0)) + { + byte b = (byte)((num7 << 4) | num8); + i += 2; + decoder.AddByte(b); + continue; + } + } + Label_0106: + if ((ch & 0xff80) == 0) + { + decoder.AddByte((byte)ch); + } + else + { + decoder.AddChar(ch); + } + } + return decoder.GetString(); + } + + private static int HexToInt(char h) + { + if ((h >= '0') && (h <= '9')) + { + return (h - '0'); + } + if ((h >= 'a') && (h <= 'f')) + { + return ((h - 'a') + 10); + } + if ((h >= 'A') && (h <= 'F')) + { + return ((h - 'A') + 10); + } + return -1; + } + + public static string UrlEncodeUnicode(string str) + { + if (str == null) + { + return null; + } + return UrlEncodeUnicodeStringToStringInternal(str, false); + } + + private static string UrlEncodeUnicodeStringToStringInternal(string s, bool ignoreAscii) + { + int length = s.Length; + StringBuilder builder = new StringBuilder(length); + for (int i = 0; i < length; i++) + { + char ch = s[i]; + if ((ch & 0xff80) == 0) + { + if (ignoreAscii || IsSafe(ch)) + { + builder.Append(ch); + } + else if (ch == ' ') + { + builder.Append('+'); + } + else + { + builder.Append('%'); + builder.Append(IntToHex((ch >> 4) & '\x000f')); + builder.Append(IntToHex(ch & '\x000f')); + } + } + else + { + builder.Append("%u"); + builder.Append(IntToHex((ch >> 12) & '\x000f')); + builder.Append(IntToHex((ch >> 8) & '\x000f')); + builder.Append(IntToHex((ch >> 4) & '\x000f')); + builder.Append(IntToHex(ch & '\x000f')); + } + } + return builder.ToString(); + } + + internal static bool IsSafe(char ch) + { + if ((((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))) || ((ch >= '0') && (ch <= '9'))) + { + return true; + } + switch (ch) + { + case '\'': + case '(': + case ')': + case '*': + case '-': + case '.': + case '_': + case '!': + return true; + } + return false; + } + + internal static char IntToHex(int n) + { + if (n <= 9) + { + return (char)(n + 0x30); + } + return (char)((n - 10) + 0x61); + } + + public static NameValueCollection ParseQueryString(string query) + { + return ParseQueryString(query, Encoding.UTF8); + } + + public static NameValueCollection ParseQueryString(string query, Encoding encoding) + { + if (query == null) + { + throw new ArgumentNullException("query"); + } + if (encoding == null) + { + throw new ArgumentNullException("encoding"); + } + if ((query.Length > 0) && (query[0] == '?')) + { + query = query.Substring(1); + } + return new HttpValueCollection(query, false, true, encoding); + } + + private class UrlDecoder + { + // Fields + private int _bufferSize; + private byte[] _byteBuffer; + private char[] _charBuffer; + private Encoding _encoding; + private int _numBytes; + private int _numChars; + + // Methods + internal UrlDecoder(int bufferSize, Encoding encoding) + { + this._bufferSize = bufferSize; + this._encoding = encoding; + this._charBuffer = new char[bufferSize]; + } + + internal void AddByte(byte b) + { + if (this._byteBuffer == null) + { + this._byteBuffer = new byte[this._bufferSize]; + } + this._byteBuffer[this._numBytes++] = b; + } + + internal void AddChar(char ch) + { + if (this._numBytes > 0) + { + this.FlushBytes(); + } + this._charBuffer[this._numChars++] = ch; + } + + private void FlushBytes() + { + if (this._numBytes > 0) + { + this._numChars += this._encoding.GetChars(this._byteBuffer, 0, this._numBytes, this._charBuffer, this._numChars); + this._numBytes = 0; + } + } + + internal string GetString() + { + if (this._numBytes > 0) + { + this.FlushBytes(); + } + if (this._numChars > 0) + { + return new string(this._charBuffer, 0, this._numChars); + } + return string.Empty; + } + } + + } + + [Serializable] + internal class HttpValueCollection : NameValueCollection + { + // Methods + internal HttpValueCollection() + : base(StringComparer.OrdinalIgnoreCase) + { + } + + internal HttpValueCollection(int capacity) + : base(capacity, (IEqualityComparer)StringComparer.OrdinalIgnoreCase) + { + } + + protected HttpValueCollection(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + internal HttpValueCollection(string str, bool readOnly, bool urlencoded, Encoding encoding) + : base(StringComparer.OrdinalIgnoreCase) + { + if (!string.IsNullOrEmpty(str)) + { + this.FillFromString(str, urlencoded, encoding); + } + base.IsReadOnly = readOnly; + } + + + internal void FillFromString(string s) + { + this.FillFromString(s, false, null); + } + + internal void FillFromString(string s, bool urlencoded, Encoding encoding) + { + int num = (s != null) ? s.Length : 0; + for (int i = 0; i < num; i++) + { + int startIndex = i; + int num4 = -1; + while (i < num) + { + char ch = s[i]; + if (ch == '=') + { + if (num4 < 0) + { + num4 = i; + } + } + else if (ch == '&') + { + break; + } + i++; + } + string str = null; + string str2 = null; + if (num4 >= 0) + { + str = s.Substring(startIndex, num4 - startIndex); + str2 = s.Substring(num4 + 1, (i - num4) - 1); + } + else + { + str2 = s.Substring(startIndex, i - startIndex); + } + if (urlencoded) + { + base.Add(HttpUtility.UrlDecode(str, encoding), HttpUtility.UrlDecode(str2, encoding)); + } + else + { + base.Add(str, str2); + } + if ((i == (num - 1)) && (s[i] == '&')) + { + base.Add(null, string.Empty); + } + } + } + + internal void MakeReadOnly() + { + base.IsReadOnly = true; + } + + internal void MakeReadWrite() + { + base.IsReadOnly = false; + } + + internal void Reset() + { + base.Clear(); + } + + public override string ToString() + { + return this.ToString(true); + } + + internal virtual string ToString(bool urlencoded) + { + return this.ToString(urlencoded, null); + } + + internal virtual string ToString(bool urlencoded, IDictionary excludeKeys) + { + int count = this.Count; + if (count == 0) + { + return string.Empty; + } + StringBuilder builder = new StringBuilder(); + bool flag = (excludeKeys != null) && (excludeKeys["__VIEWSTATE"] != null); + for (int i = 0; i < count; i++) + { + string key = this.GetKey(i); + if (((!flag || (key == null)) || !key.StartsWith("__VIEWSTATE", StringComparison.Ordinal)) && (((excludeKeys == null) || (key == null)) || (excludeKeys[key] == null))) + { + string str3; + if (urlencoded) + { + key = HttpUtility.UrlEncodeUnicode(key); + } + string str2 = !string.IsNullOrEmpty(key) ? (key + "=") : string.Empty; + ArrayList list = (ArrayList)base.BaseGet(i); + int num3 = (list != null) ? list.Count : 0; + if (builder.Length > 0) + { + builder.Append('&'); + } + if (num3 == 1) + { + builder.Append(str2); + str3 = (string)list[0]; + if (urlencoded) + { + str3 = HttpUtility.UrlEncodeUnicode(str3); + } + builder.Append(str3); + } + else if (num3 == 0) + { + builder.Append(str2); + } + else + { + for (int j = 0; j < num3; j++) + { + if (j > 0) + { + builder.Append('&'); + } + builder.Append(str2); + str3 = (string)list[j]; + if (urlencoded) + { + str3 = HttpUtility.UrlEncodeUnicode(str3); + } + builder.Append(str3); + } + } + } + } + return builder.ToString(); + } + } + #endregion + +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Download/Downloader.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Download/Downloader.cs new file mode 100644 index 000000000000..435ad3fafa2f --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Download/Downloader.cs @@ -0,0 +1,168 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Sync.Threading; +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.ServiceModel.Channels; + +namespace Microsoft.WindowsAzure.Commands.Sync.Download +{ + public class Downloader + { + private const int DefaultConnectionLimit = 24; + private DownloaderParameters parameters; + + public Downloader(BlobUri blobUri, string storageAccountKey, string locaFilePath) + { + this.parameters = new DownloaderParameters + { + BlobUri = blobUri, + LocalFilePath = locaFilePath, + ConnectionLimit = DefaultConnectionLimit, + StorageAccountKey = storageAccountKey, + ValidateFreeDiskSpace = false, + ProgressDownloadStatus = Program.SyncOutput.ProgressDownloadStatus, + ProgressDownloadComplete = Program.SyncOutput.ProgressDownloadComplete + }; + } + + public Downloader(DownloaderParameters parameters) + { + this.parameters = parameters; + } + + public void Download() + { + if (parameters.OverWrite) + { + DeleteTempVhdIfExist(parameters.LocalFilePath); + } + else + { + if (File.Exists(parameters.LocalFilePath)) + { + var message = String.Format("File already exists, you can use Overwrite option to delete it:'{0}'", parameters.LocalFilePath); + throw new ArgumentException(message); + } + } + + var blobHandle = new BlobHandle(parameters.BlobUri, this.parameters.StorageAccountKey); + + if (parameters.ValidateFreeDiskSpace) + { + TryValidateFreeDiskSpace(parameters.LocalFilePath, blobHandle.Length); + } + + const int megaByte = 1024 * 1024; + + var ranges = blobHandle.GetUploadableRanges(); + var bufferManager = BufferManager.CreateBufferManager(Int32.MaxValue, 20 * megaByte); + var downloadStatus = new ProgressStatus(0, ranges.Sum(r => r.Length), new ComputeStats()); + + Trace.WriteLine(String.Format("Total Data:{0}", ranges.Sum(r => r.Length))); + + Program.SyncOutput.WriteVerboseWithTimestamp("Downloading the blob: {0}", parameters.BlobUri.BlobName); + + var fileStreamLock = new object(); + using (new ServicePointHandler(parameters.BlobUri.Uri, parameters.ConnectionLimit)) + { + using (new ProgressTracker(downloadStatus, parameters.ProgressDownloadStatus, parameters.ProgressDownloadComplete, TimeSpan.FromSeconds(1))) + { + using (var fileStream = new FileStream(parameters.LocalFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write, 8 * megaByte, FileOptions.WriteThrough)) + { + fileStream.SetLength(0); + fileStream.SetLength(blobHandle.Length); + + LoopResult lr = Parallel.ForEach(ranges, + blobHandle.OpenStream, + (r, b) => + { + b.Seek(r.StartIndex, SeekOrigin.Begin); + + byte[] buffer = this.EnsureReadAsSize(b, (int)r.Length, bufferManager); + + lock (fileStreamLock) + { + Trace.WriteLine(String.Format("Range:{0}", r)); + fileStream.Seek(r.StartIndex, SeekOrigin.Begin); + fileStream.Write(buffer, 0, (int)r.Length); + fileStream.Flush(); + } + + downloadStatus.AddToProcessedBytes((int)r.Length); + }, + pbwlf => + { + pbwlf.Dispose(); + }, + parameters.ConnectionLimit); + + if (lr.IsExceptional) + { + throw new AggregateException(lr.Exceptions); + } + } + } + } + Program.SyncOutput.WriteVerboseWithTimestamp("Blob downloaded successfullty: {0}", parameters.BlobUri.BlobName); + } + + private void TryValidateFreeDiskSpace(string destination, long blobLength) + { + try + { + DriveInfo info = new DriveInfo(destination); + if (info.AvailableFreeSpace < blobLength) + { + string message = String.Format("Insufficient disk space: Blob's size is {0}, however available space is {1}.", blobLength, info.AvailableFreeSpace); + throw new ArgumentOutOfRangeException(message); + } + } + catch (Exception) + { + } + } + + private void DeleteTempVhdIfExist(string fileName) + { + if (File.Exists(fileName)) + File.Delete(fileName); + } + + private byte[] EnsureReadAsSize(Stream stream, int size, BufferManager manager) + { + byte[] buffer = manager.TakeBuffer(size); + int byteRead = 0; + int totalRead = 0; + int sizeLeft = size; + do + { + byteRead = stream.Read(buffer, totalRead, sizeLeft); + totalRead += byteRead; + if (totalRead == size) + { + break; + } + + sizeLeft = sizeLeft - byteRead; + } while (true); + + return buffer; + } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Download/DownloaderParameters.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Download/DownloaderParameters.cs new file mode 100644 index 000000000000..cb835753c7f7 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Download/DownloaderParameters.cs @@ -0,0 +1,30 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Sync.Download +{ + public class DownloaderParameters + { + public BlobUri BlobUri { get; set; } + public string LocalFilePath { get; set; } + public int ConnectionLimit { get; set; } + public string StorageAccountKey { get; set; } + public bool ValidateFreeDiskSpace { get; set; } + public bool OverWrite { get; set; } + public Action ProgressDownloadStatus { get; set; } + public Action ProgressDownloadComplete { get; set; } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/IO/StreamWithReadProgress.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/IO/StreamWithReadProgress.cs new file mode 100644 index 000000000000..983776b1d4bd --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/IO/StreamWithReadProgress.cs @@ -0,0 +1,104 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.IO; + +namespace Microsoft.WindowsAzure.Commands.Sync.IO +{ + internal class StreamWithReadProgress : Stream + { + private readonly Stream innerStream; + private readonly TimeSpan progressInterval; + private ProgressStatus readStatus; + private ProgressTracker progressTracker; + + public StreamWithReadProgress(Stream innerStream, TimeSpan progressInterval) + { + this.innerStream = innerStream; + this.progressInterval = progressInterval; + this.readStatus = new ProgressStatus(0, this.innerStream.Length, new ComputeStats()); + + this.progressTracker = new ProgressTracker(this.readStatus, + Program.SyncOutput.ProgressOperationStatus, + Program.SyncOutput.ProgressOperationComplete, + this.progressInterval); + } + + public override void Flush() + { + this.innerStream.Flush(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return this.innerStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + this.innerStream.SetLength(value); + } + + public override int Read(byte[] buffer, int offset, int count) + { + var readCount = this.innerStream.Read(buffer, offset, count); + readStatus.AddToProcessedBytes(readCount); + return readCount; + } + + public override void Write(byte[] buffer, int offset, int count) + { + this.innerStream.Write(buffer, offset, count); + } + + public override bool CanRead + { + get { return this.innerStream.CanRead; } + } + + public override bool CanSeek + { + get { return this.innerStream.CanSeek; } + } + + public override bool CanWrite + { + get { return this.innerStream.CanWrite; } + } + + public override long Length + { + get { return this.innerStream.Length; } + } + + public override long Position + { + get { return this.innerStream.Position; } + set { this.innerStream.Position = value; } + } + + public override void Close() + { + this.progressTracker.Dispose(); + this.innerStream.Close(); + } + + protected override void Dispose(bool disposing) + { + this.progressTracker.Dispose(); + this.innerStream.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Program.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Program.cs new file mode 100644 index 000000000000..07246682d517 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Program.cs @@ -0,0 +1,80 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using System; +using System.Collections.Generic; + +namespace Microsoft.WindowsAzure.Commands.Sync +{ + public class Program + { + static public ISyncOutputEvents SyncOutput + { + get + { + return RawEvents; + } + set + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + RawEvents = value; + } + } + // static public IProgramConfiguration Configuration { get; private set; } + //private static ISyncOutputEvents RawEvents = new SyncOutputEvents(); + private static ISyncOutputEvents RawEvents = null; + } + + public interface ISyncOutputEvents + { + void MessageCreatingNewPageBlob(long pageBlobSize); + void MessageResumingUpload(); + void ErrorUploadFailedWithExceptions(IList exjceptions); + + + void ProgressCopyComplete(TimeSpan elapsed); + void ProgressCopyStatus(double precentComplete, double avgThroughputMbps, TimeSpan remainingTime); + void ProgressCopyStatus(ProgressRecord record); + + void ProgressUploadStatus(ProgressRecord record); + void ProgressUploadStatus(double precentComplete, double avgThroughputMbps, TimeSpan remainingTime); + void ProgressUploadComplete(TimeSpan elapsed); + + void ProgressDownloadStatus(ProgressRecord record); + void ProgressDownloadStatus(double precentComplete, double avgThroughputMbps, TimeSpan remainingTime); + void ProgressDownloadComplete(TimeSpan elapsed); + + void ProgressOperationStatus(ProgressRecord record); + void ProgressOperationStatus(double percentComplete, double avgThroughputMbps, TimeSpan remainingTime); + void ProgressOperationComplete(TimeSpan elapsed); + + void MessageCalculatingMD5Hash(string filePath); + void MessageMD5HashCalculationFinished(); + + void MessageRetryingAfterANetworkDisruption(); + void DebugRetryingAfterException(Exception lastException); + + void MessageDetectingActualDataBlocks(); + void MessageDetectingActualDataBlocksCompleted(); + void MessagePrintBlockRange(IndexRange range); + void DebugEmptyBlockDetected(IndexRange range); + void ProgressEmptyBlockDetection(int processedRangeCount, int totalRangeCount); + + void WriteVerboseWithTimestamp(string message, params object[] args); + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/ProgressRecord.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/ProgressRecord.cs new file mode 100644 index 000000000000..92940867a2d4 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/ProgressRecord.cs @@ -0,0 +1,25 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Sync +{ + public class ProgressRecord + { + public double PercentComplete { get; set; } + public double AvgThroughputMbPerSecond { get; set; } + public TimeSpan RemainingTime { get; set; } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/ProgressStatus.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/ProgressStatus.cs new file mode 100644 index 000000000000..0b7af8c74301 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/ProgressStatus.cs @@ -0,0 +1,104 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Sync +{ + public class ProgressStatus + { + const double MB = 1024.0 * 1024.0; + readonly object thisLock = new object(); + + public ProgressStatus(long alreadyProcessedBytes, long totalLength) : this(alreadyProcessedBytes, totalLength, new ComputeStats()) + { + } + + public ProgressStatus(long alreadyProcessedBytes, long totalLength, ComputeStats computeStats) + { + this.PreExistingBytes = alreadyProcessedBytes; + this.BytesProcessed = alreadyProcessedBytes; + this.TotalLength = totalLength; + this.ThrougputStats = computeStats; + this.StartTime = DateTime.UtcNow; + } + + long PreExistingBytes { get; set; } + internal long BytesProcessed { get; private set; } + long TotalLength { get; set; } + DateTime StartTime { get; set; } + ComputeStats ThrougputStats { get; set; } + + public bool TryGetProgressRecord(out ProgressRecord record) + { + record = null; + lock (thisLock) + { + if (HasProgess()) + { + record = Progress(); + return true; + } + } + return false; + } + + public void AddToProcessedBytes(long size) + { + lock (thisLock) + { + this.BytesProcessed += size; + } + } + + bool HasProgess() + { + return this.BytesProcessed > this.PreExistingBytes; + } + + ProgressRecord Progress() + { + double computeAvg = ThrougputStats.ComputeAvg(ThroughputMBs()); + double avtThroughputMbps = 8.0 * computeAvg; + double remainingSeconds = (RemainingMB() / computeAvg); + var pr = new ProgressRecord + { + PercentComplete = PercentComplete(), + AvgThroughputMbPerSecond = avtThroughputMbps, + RemainingTime = TimeSpan.FromSeconds(remainingSeconds) + }; + return pr; + } + + double RemainingMB() + { + return (this.TotalLength - this.BytesProcessed) / MB; + } + + double ThroughputMBs() + { + return (this.BytesProcessed - this.PreExistingBytes) / MB / ProcessTime().TotalSeconds; + } + + TimeSpan ProcessTime() + { + return DateTime.UtcNow - this.StartTime; + } + + double PercentComplete() + { + return 100.0 * this.BytesProcessed / ((double)this.TotalLength); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/ProgressTracker.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/ProgressTracker.cs new file mode 100644 index 000000000000..8188a0324f6c --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/ProgressTracker.cs @@ -0,0 +1,117 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Timers; + +namespace Microsoft.WindowsAzure.Commands.Sync +{ + public class ProgressTracker : IDisposable + { + private readonly ProgressStatus progressStatus; + private readonly Action progress; + private readonly Action complete; + private readonly TimeSpan progressInterval; + private Timer progressTimer; + private object thisLock = new object(); + private ElapsedEventHandler progressTimerOnElapsed; + private Stopwatch stopWatch; + private bool isDisposed; + + public ProgressTracker(ProgressStatus progressStatus) : + this(progressStatus, Program.SyncOutput.ProgressUploadStatus, Program.SyncOutput.ProgressUploadComplete, TimeSpan.FromSeconds(1)) + { + } + + public ProgressTracker(ProgressStatus progressStatus, Action progress, Action complete, TimeSpan progressInterval) + { + this.progressStatus = progressStatus; + this.progress = progress; + this.complete = complete; + this.progressInterval = progressInterval; + this.stopWatch = new Stopwatch(); + InitilizeProgressTimer(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Lifetime of timer is bound to ProgressTracker")] + private void InitilizeProgressTimer() + { + stopWatch.Start(); + bool throwing = false; + try + { + progressTimer = new Timer + { + AutoReset = false, + Interval = progressInterval.TotalMilliseconds + }; + progressTimerOnElapsed = (sender, args) => + { + ProgressRecord pr; + if (progressStatus.TryGetProgressRecord(out pr)) + { + this.progress(pr); + } + progressTimer.Enabled = true; + }; + progressTimer.Elapsed += progressTimerOnElapsed; + progressTimer.Enabled = true; + } + catch (Exception) + { + throwing = true; + throw; + } + finally + { + if (throwing && progressTimer != null) + { + progressTimer.Elapsed -= progressTimerOnElapsed; + progressTimer.Enabled = false; + progressTimer.Dispose(); + progressTimer = null; + } + } + } + + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (isDisposed) + { + return; + } + if (disposing) + { + progressTimer.Elapsed -= progressTimerOnElapsed; + progressTimer.Enabled = false; + stopWatch.Stop(); + if (stopWatch.Elapsed != TimeSpan.Zero) + { + this.complete(stopWatch.Elapsed); + } + progressTimer.Dispose(); + this.isDisposed = true; + } + } + + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/ServicePointHandler.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/ServicePointHandler.cs new file mode 100644 index 000000000000..fdc2def89d4a --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/ServicePointHandler.cs @@ -0,0 +1,59 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Net; +using System.Reflection; + +namespace Microsoft.WindowsAzure.Commands.Sync +{ + internal class ServicePointHandler : IDisposable + { + private bool disposed; + private ServicePoint servicePoint; + private int originalConnectionLimit; + + public ServicePointHandler(Uri uri, int connectionLimit) + { + this.servicePoint = ServicePointManager.FindServicePoint(uri, GetWebProxy()); + this.originalConnectionLimit = servicePoint.ConnectionLimit; + servicePoint.ConnectionLimit = connectionLimit; + } + + private static IWebProxy GetWebProxy() + { + Type webRequestType = typeof(WebRequest); + PropertyInfo propertyInfo = webRequestType.GetProperty("InternalDefaultWebProxy", BindingFlags.Static | BindingFlags.NonPublic); + return (IWebProxy)propertyInfo.GetValue(null, null); + } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + this.servicePoint.ConnectionLimit = originalConnectionLimit; + } + disposed = true; + } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Threading/Parallel.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Threading/Parallel.cs new file mode 100644 index 000000000000..775b8be896d7 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Threading/Parallel.cs @@ -0,0 +1,240 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Microsoft.WindowsAzure.Commands.Sync.Threading +{ + internal class Parallel + { + public readonly static int MaxParallellism = Environment.ProcessorCount; + + public static LoopResult ForEach(IEnumerable source, Func argumentConstructor, Action body) + { + return ForEach(source, argumentConstructor, body, MaxParallellism); + } + + public static LoopResult ForEach(IEnumerable source, Func argumentConstructor, Action body, int parallelism) + { + var loopResult = new InternalLoopResult(); + int numProcs = parallelism; + int remainingWorkItems = numProcs; + using (var enumerator = source.GetEnumerator()) + { + using (var mre = new ManualResetEvent(false)) + { + // Create each of the work items. + for (int p = 0; p < numProcs; p++) + { + ThreadPool.QueueUserWorkItem(delegate + { + try + { + + TA argument = argumentConstructor(); + // Iterate until there's no more work. + while (true) + { + // Get the next item under a lock, + // then process that item. + T nextItem; + lock (enumerator) + { + if (!enumerator.MoveNext()) break; + nextItem = enumerator.Current; + } + body(nextItem, argument); + } + } + catch (Exception e) + { + loopResult.AddException(e); + } + if (Interlocked.Decrement(ref remainingWorkItems) == 0) + mre.Set(); + }); + } + // Wait for all threads to complete. + mre.WaitOne(); + loopResult.SetCompleted(); + } + } + return loopResult; + } + + public static LoopResult ForEach(IEnumerable source, Action body) + { + return ForEach(source, body, MaxParallellism); + } + + public static LoopResult ForEach(IEnumerable source, Action body, int parallelism) + { + var loopResult = new InternalLoopResult(); + int numProcs = parallelism; + int remainingWorkItems = numProcs; + using (var enumerator = source.GetEnumerator()) + { + using (var mre = new ManualResetEvent(false)) + { + // Create each of the work items. + for (int p = 0; p < numProcs; p++) + { + ThreadPool.QueueUserWorkItem(delegate + { + // Iterate until there's no more work. + try + { + while (true) + { + // Get the next item under a lock, + // then process that item. + T nextItem; + lock (enumerator) + { + if (!enumerator.MoveNext()) break; + nextItem = enumerator.Current; + } + body(nextItem); + } + } + catch (Exception e) + { + loopResult.AddException(e); + } + if (Interlocked.Decrement(ref remainingWorkItems) == 0) + mre.Set(); + }); + } + // Wait for all threads to complete. + mre.WaitOne(); + loopResult.SetCompleted(); + } + } + return loopResult; + } + + public static LoopResult ForEach(IEnumerable source, Func argumentConstructor, Action body, Action finalize, int parallelism) + { + var loopResult = new InternalLoopResult(); + int numProcs = parallelism; + int remainingWorkItems = numProcs; + + IList arguments = new List(); + using (var enumerator = source.GetEnumerator()) + { + using (var mre = new ManualResetEvent(false)) + { + // Create each of the work items. + for (int p = 0; p < numProcs; p++) + { + ThreadPool.QueueUserWorkItem(delegate + { + try + { + TA argument = argumentConstructor(); + lock (arguments) + { + arguments.Add(argument); + } + // Iterate until there's no more work. + while (true && !loopResult.IsExceptional) + { + // Get the next item under a lock, + // then process that item. + T nextItem; + lock (enumerator) + { + if (!enumerator.MoveNext()) break; + nextItem = enumerator.Current; + } + body(nextItem, argument); + } + } + catch (Exception e) + { + loopResult.AddException(e); + } + if (Interlocked.Decrement(ref remainingWorkItems) == 0) + mre.Set(); + }); + } + // Wait for all threads to complete. + mre.WaitOne(); + + foreach (var argument in arguments) + { + finalize(argument); + } + + loopResult.SetCompleted(); + } + } + return loopResult; + } + + private class InternalLoopResult : LoopResult + { + private IList exceptions; + private object lockObject = new object(); + + public InternalLoopResult() + { + this.exceptions = new List(); + } + + public override bool IsCompleted + { + get; protected set; + } + + public override IList Exceptions + { + get { return new List(exceptions); } + } + + public override bool IsExceptional + { + get + { + lock (lockObject) + { + return this.exceptions.Count > 0; + } + } + } + + public void SetCompleted() + { + this.IsCompleted = true; + } + + public void AddException(Exception exception) + { + lock (lockObject) + { + this.exceptions.Add(exception); + } + } + } + } + + public abstract class LoopResult + { + public abstract bool IsCompleted { get; protected set; } + public abstract IList Exceptions { get; } + public abstract bool IsExceptional { get; } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobCreator.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobCreator.cs new file mode 100644 index 000000000000..fb95bab864c1 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobCreator.cs @@ -0,0 +1,47 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Sync.Download; +using System.IO; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + public class BlobCreator : BlobCreatorBase + { + public BlobCreator(FileInfo localVhd, BlobUri destination, ICloudPageBlobObjectFactory blobObjectFactory, bool overWrite) : + base(localVhd, destination, blobObjectFactory, overWrite) + { + } + + protected override void CreateRemoteBlobAndPopulateContext(UploadContext context) + { + CreateRemoteBlob(); + PopulateContextWithUploadableRanges(localVhd, context, false); + PopulateContextWithDataToUpload(localVhd, context); + } + + private void CreateRemoteBlob() + { + Program.SyncOutput.MessageCreatingNewPageBlob(OperationMetaData.FileMetaData.VhdSize); + + destinationBlob.CreateAsync(OperationMetaData.FileMetaData.VhdSize).ConfigureAwait(false).GetAwaiter().GetResult(); + + using (var bdms = new BlobMetaDataScope(destinationBlob)) + { + bdms.Current.SetUploadMetaData(OperationMetaData); + bdms.Complete(); + } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobCreatorBase.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobCreatorBase.cs new file mode 100644 index 000000000000..26cf0c737d88 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobCreatorBase.cs @@ -0,0 +1,402 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Sync.Download; +using Microsoft.WindowsAzure.Commands.Tools.Vhd; +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using Microsoft.WindowsAzure.Storage.Auth; +using Microsoft.WindowsAzure.Storage.Blob; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Security.Permissions; +using System.ServiceModel.Channels; +using System.Text; +using System.Threading; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + public interface ICloudPageBlobObjectFactory + { + CloudPageBlob Create(BlobUri destination); + bool CreateContainer(BlobUri destination); + BlobRequestOptions CreateRequestOptions(); + } + + public abstract class BlobCreatorBase + { + private const long FourTeraByte = 4 * 1024L * 1024L * 1024L * 1024L; + + protected FileInfo localVhd; + protected readonly ICloudPageBlobObjectFactory blobObjectFactory; + protected Uri destination; + protected BlobUri blobDestination; + protected string queryString; + protected StorageCredentials credentials; + protected CloudPageBlob destinationBlob; + protected BlobRequestOptions requestOptions; + protected bool overWrite; + private readonly TimeSpan delayBetweenRetries = TimeSpan.FromSeconds(10); + + private const int MegaByte = 1024 * 1024; + private const int PageSizeInBytes = 2 * MegaByte; + private const int MaxBufferSize = 20 * MegaByte; + + protected BlobCreatorBase(FileInfo localVhd, BlobUri blobDestination, ICloudPageBlobObjectFactory blobObjectFactory, bool overWrite) + { + this.localVhd = localVhd; + this.blobObjectFactory = blobObjectFactory; + this.destination = new Uri(blobDestination.BlobPath); + this.blobDestination = blobDestination; + this.overWrite = overWrite; + + this.destinationBlob = blobObjectFactory.Create(blobDestination); + this.requestOptions = this.blobObjectFactory.CreateRequestOptions(); + } + + private LocalMetaData operationMetaData; + + public LocalMetaData OperationMetaData + { + [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + get + { + if (this.operationMetaData == null) + { + this.operationMetaData = new LocalMetaData + { + FileMetaData = FileMetaData.Create(localVhd.FullName), + SystemInformation = SystemInformation.Create() + }; + } + return operationMetaData; + } + } + + public byte[] MD5HashOfLocalVhd + { + get { return OperationMetaData.FileMetaData.MD5Hash; } + } + + private static void AssertIfValidVhdSize(FileInfo fileInfo) + { + using (var stream = new VirtualDiskStream(fileInfo.FullName)) + { + if (stream.Length > FourTeraByte) + { + var lengthString = stream.Length.ToString("N0", CultureInfo.CurrentCulture); + var expectedLengthString = FourTeraByte.ToString("N0", CultureInfo.CurrentCulture); + string message = String.Format("VHD size is too large ('{0}'), maximum allowed size is '{1}'.", lengthString, expectedLengthString); + throw new InvalidOperationException(message); + } + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Need to keep UploadContext till the end of upload")] + public UploadContext Create() + { + AssertIfValidhVhd(localVhd); + AssertIfValidVhdSize(localVhd); + + this.blobObjectFactory.CreateContainer(blobDestination); + + UploadContext context = null; + bool completed = false; + try + { + context = new UploadContext + { + DestinationBlob = destinationBlob, + SingleInstanceMutex = AcquireSingleInstanceMutex(destinationBlob.Uri) + }; + + if (overWrite) + { + destinationBlob.DeleteIfExistsAsync(DeleteSnapshotsOption.IncludeSnapshots, null, requestOptions, operationContext: null) + .ConfigureAwait(false).GetAwaiter().GetResult(); + } + + if (destinationBlob.Exists(requestOptions)) + { + Program.SyncOutput.MessageResumingUpload(); + + if (destinationBlob.GetBlobMd5Hash(requestOptions) != null) + { + throw new InvalidOperationException( + "An image already exists in blob storage with this name. If you want to upload again, use the Overwrite option."); + } + var metaData = destinationBlob.GetUploadMetaData(); + + AssertMetaDataExists(metaData); + AssertMetaDataMatch(metaData, OperationMetaData); + + PopulateContextWithUploadableRanges(localVhd, context, true); + PopulateContextWithDataToUpload(localVhd, context); + } + else + { + CreateRemoteBlobAndPopulateContext(context); + } + context.Md5HashOfLocalVhd = MD5HashOfLocalVhd; + completed = true; + } + finally + { + if (!completed && context != null) + { + context.Dispose(); + } + } + return context; + } + + public static void AssertIfValidhVhd(FileInfo vhdFile) + { + var vhdValidationResults = VhdValidator.Validate(VhdValidationType.IsVhd, vhdFile.FullName); + if (vhdValidationResults.Count(r => r.ErrorCode != 0) != 0) + { + string message = String.Format("'{0}' is not a valid VHD file.", vhdFile.FullName); + throw new InvalidOperationException(message, vhdValidationResults[0].Error); + } + } + + protected abstract void CreateRemoteBlobAndPopulateContext(UploadContext context); + + protected static void PopulateContextWithUploadableRanges(FileInfo vhdFile, UploadContext context, bool resume) + { + using (var vds = new VirtualDiskStream(vhdFile.FullName)) + { + IEnumerable ranges = vds.Extents.Select(e => e.Range).ToArray(); + + var bs = new BufferedStream(vds); + if (resume) + { + var alreadyUploadedRanges = context.DestinationBlob.GetPageRangesAsync() + .ConfigureAwait(false).GetAwaiter().GetResult() + .Select(pr => new IndexRange(pr.StartOffset, pr.EndOffset)); + ranges = IndexRange.SubstractRanges(ranges, alreadyUploadedRanges); + context.AlreadyUploadedDataSize = alreadyUploadedRanges.Sum(ir => ir.Length); + } + var uploadableRanges = IndexRangeHelper.ChunkRangesBySize(ranges, PageSizeInBytes).ToArray(); + if (vds.DiskType == DiskType.Fixed) + { + var nonEmptyUploadableRanges = GetNonEmptyRanges(bs, uploadableRanges).ToArray(); + context.UploadableDataSize = nonEmptyUploadableRanges.Sum(r => r.Length); + context.UploadableRanges = nonEmptyUploadableRanges; + } + else + { + context.UploadableDataSize = uploadableRanges.Sum(r => r.Length); + context.UploadableRanges = uploadableRanges; + } + } + } + + protected static void PopulateContextWithDataToUpload(FileInfo vhdFile, UploadContext context) + { + context.UploadableDataWithRanges = GetDataWithRangesToUpload(vhdFile, context); + } + + protected static IEnumerable GetNonEmptyRanges(Stream stream, IEnumerable uploadableRanges) + { + Program.SyncOutput.MessageDetectingActualDataBlocks(); + var manager = BufferManager.CreateBufferManager(Int32.MaxValue, MaxBufferSize); + int totalRangeCount = uploadableRanges.Count(); + int processedRangeCount = 0; + foreach (var range in uploadableRanges) + { + var dataWithRange = new DataWithRange(manager) + { + Data = ReadBytes(stream, range, manager), + Range = range + }; + using (dataWithRange) + { + if (dataWithRange.IsAllZero()) + { + Program.SyncOutput.DebugEmptyBlockDetected(dataWithRange.Range); + } + else + { + yield return dataWithRange.Range; + } + } + Program.SyncOutput.ProgressEmptyBlockDetection(++processedRangeCount, totalRangeCount); + } + + Program.SyncOutput.MessageDetectingActualDataBlocksCompleted(); + yield break; + } + + protected static IEnumerable GetDataWithRangesToUpload(FileInfo vhdFile, UploadContext context) + { + var uploadableRanges = context.UploadableRanges; + var manager = BufferManager.CreateBufferManager(Int32.MaxValue, MaxBufferSize); + using (var vds = new VirtualDiskStream(vhdFile.FullName)) + { + foreach (var range in uploadableRanges) + { + var localRange = range; + yield return new DataWithRange(manager) + { + Data = ReadBytes(vds, localRange, manager), + Range = localRange + }; + } + } + yield break; + } + + private static byte[] ReadBytes(Stream stream, IndexRange rangeToRead, BufferManager manager) + { + stream.Seek(rangeToRead.StartIndex, SeekOrigin.Begin); + + var bufferSize = (int)rangeToRead.Length; + var buffer = manager.TakeBuffer(bufferSize); + + for (int bytesRead = stream.Read(buffer, 0, bufferSize); + bytesRead < bufferSize; + bytesRead += stream.Read(buffer, bytesRead, bufferSize - bytesRead)) + { + } + return buffer; + } + + + private static void AssertMetaDataExists(LocalMetaData blobMetaData) + { + if (blobMetaData == null) + { + throw new InvalidOperationException("There is no CsUpload metadata on the blob, so CsUpload cannot resume. Use the overwrite option."); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Need to keep Mutex open till the end of upload")] + private static Mutex AcquireSingleInstanceMutex(Uri destinationBlobUri) + { + string mutexName = GetMutexName(destinationBlobUri); + + bool throwing = true; + Mutex singleInstanceMutex = null; + try + { + singleInstanceMutex = new Mutex(false, @"Global\" + mutexName); + if (!singleInstanceMutex.WaitOne(TimeSpan.FromSeconds(5), false)) + { + var message = String.Format("An upload is already in progress on this machine"); + throw new InvalidOperationException(message); + } + throwing = false; + return singleInstanceMutex; + } + finally + { + if (throwing && singleInstanceMutex != null) + { + singleInstanceMutex.ReleaseMutex(); + singleInstanceMutex.Close(); + } + } + } + + private static string GetMutexName(Uri destinationBlobUri) + { + var invariant = destinationBlobUri.ToString().ToLowerInvariant(); + var bytes = Encoding.Unicode.GetBytes(invariant); + using (var md5 = MD5.Create()) + { + byte[] hash = md5.ComputeHash(bytes); + return Convert.ToBase64String(hash); + } + } + + private static void AssertMetaDataMatch(LocalMetaData blobMetaData, LocalMetaData localMetaData) + { + var systemInformation = blobMetaData.SystemInformation; + + if (String.Compare(systemInformation.MachineName, Environment.MachineName, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase) != 0) + { + var message = String.Format("An upload is already in progress on machine {0} with process id {1}", + systemInformation.MachineName, + systemInformation.CsUploadProcessId); + + throw new InvalidOperationException(message); + } + + var fileMetaDataMessages = CompareFileMetaData(blobMetaData.FileMetaData, localMetaData.FileMetaData); + + if (fileMetaDataMessages.Count > 0) + { + throw new InvalidOperationException(fileMetaDataMessages.Aggregate((r, n) => r + Environment.NewLine + n)); + } + } + + private static List CompareFileMetaData(FileMetaData blobFileMetaData, FileMetaData localFileMetaData) + { + var fileMetaDataMessages = new List(); + if (blobFileMetaData.VhdSize != localFileMetaData.VhdSize) + { + var message = String.Format("Logical size of VHD file in blob storage ({0}) and local VHD file ({1}) does not match ", + blobFileMetaData.VhdSize, + localFileMetaData.VhdSize); + fileMetaDataMessages.Add(message); + } + + if (blobFileMetaData.Size != localFileMetaData.Size) + { + var message = String.Format("Size of VHD file in blob storage ({0}) and local VHD file ({1}) does not match ", + blobFileMetaData.Size, + localFileMetaData.Size); + fileMetaDataMessages.Add(message); + } + + if (!blobFileMetaData.MD5Hash.SequenceEqual(localFileMetaData.MD5Hash)) + { + var message = String.Format("MD5 hash of VHD file in blob storage ({0}) and local VHD file ({1}) does not match ", + blobFileMetaData.MD5Hash.ToString(","), + localFileMetaData.MD5Hash.ToString(",")); + fileMetaDataMessages.Add(message); + } + + + if (DateTime.Compare(blobFileMetaData.LastModifiedDateUtc, localFileMetaData.LastModifiedDateUtc) != 0) + { + var message = String.Format("Last modified date of VHD file in blob storage ({0}) and local VHD file ({1}) does not match ", + blobFileMetaData.LastModifiedDateUtc, + localFileMetaData.LastModifiedDateUtc); + fileMetaDataMessages.Add(message); + } + + if (blobFileMetaData.FileFullName != localFileMetaData.FileFullName) + { + var message = String.Format("Full name of VHD file in blob storage ({0}) and local VHD file ({1}) does not match ", + blobFileMetaData.FileFullName, + localFileMetaData.FileFullName); + fileMetaDataMessages.Add(message); + } + + if (blobFileMetaData.CreatedDateUtc != localFileMetaData.CreatedDateUtc) + { + var message = String.Format("Full name of VHD file in blob storage ({0}) and local VHD file ({1}) does not match ", + blobFileMetaData.CreatedDateUtc, + localFileMetaData.CreatedDateUtc); + fileMetaDataMessages.Add(message); + } + return fileMetaDataMessages; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobSynchronizer.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobSynchronizer.cs new file mode 100644 index 000000000000..c8116243f30e --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/BlobSynchronizer.cs @@ -0,0 +1,102 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Sync.Threading; +using Microsoft.WindowsAzure.Storage.Blob; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + public class BlobSynchronizer + { + private readonly UploadContext context; + private byte[] md5Hash; + private readonly IEnumerable dataWithRanges; + private readonly int maxParallelism; + private readonly long dataToUpload; + private readonly long alreadyUploadedData; + private CloudPageBlob blob; + + public BlobSynchronizer(UploadContext context, int maxParallelism) + { + this.context = context; + this.md5Hash = context.Md5HashOfLocalVhd; + this.dataWithRanges = context.UploadableDataWithRanges; + this.dataToUpload = context.UploadableDataSize; + this.alreadyUploadedData = context.AlreadyUploadedDataSize; + this.blob = context.DestinationBlob; + this.maxParallelism = maxParallelism; + } + + public bool Synchronize() + { + var uploadStatus = new ProgressStatus(alreadyUploadedData, alreadyUploadedData + dataToUpload, new ComputeStats()); + + using (new ServicePointHandler(blob.Uri, this.maxParallelism)) + using (new ProgressTracker(uploadStatus)) + { + var loopResult = Parallel.ForEach(dataWithRanges, + () => new CloudPageBlob(blob.Uri, blob.ServiceClient.Credentials), + (dwr, b) => + { + using (dwr) + { + var md5HashOfDataChunk = GetBase64EncodedMd5Hash(dwr.Data, (int)dwr.Range.Length); + using (var stream = new MemoryStream(dwr.Data, 0, (int)dwr.Range.Length)) + { + b.Properties.ContentMD5 = md5HashOfDataChunk; + b.WritePagesAsync(stream, dwr.Range.StartIndex, contentMD5: null) + .ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + uploadStatus.AddToProcessedBytes((int)dwr.Range.Length); + }, this.maxParallelism); + if (loopResult.IsExceptional) + { + if (loopResult.Exceptions.Any()) + { + Program.SyncOutput.ErrorUploadFailedWithExceptions(loopResult.Exceptions); + + throw new AggregateException(loopResult.Exceptions); + } + } + else + { + using (var bdms = new BlobMetaDataScope(new CloudPageBlob(blob.Uri, blob.ServiceClient.Credentials))) + { + bdms.Current.SetBlobMd5Hash(md5Hash); + bdms.Current.CleanUpUploadMetaData(); + bdms.Complete(); + } + } + } + return true; + } + + private static string GetBase64EncodedMd5Hash(byte[] data, int length) + { + using (var hash = MD5.Create()) + { + hash.TransformBlock(data, 0, length, null, 0); + hash.TransformFinalBlock(new byte[0], 0, 0); + var bytes = hash.Hash; + return Convert.ToBase64String(bytes); + } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/ComputeStats.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/ComputeStats.cs new file mode 100644 index 000000000000..1c3b70be0ef4 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/ComputeStats.cs @@ -0,0 +1,51 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace Microsoft.WindowsAzure.Commands.Tools.CsUpload +{ + internal class ComputeStats + { + IList history; + int historySize; + + public ComputeStats() : this(60) + { + } + + public ComputeStats(int historySize) + { + history = new List(historySize); + this.historySize = historySize; + } + + public double ComputeAvg(double current) + { + if (history.Count > historySize) + { + history.RemoveAt(0); + } + history.Add(current); + double sum = 0.0; + foreach (var x in history) + { + sum += x; + } + return sum / history.Count; + } + } +} + + diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/DataWithRange.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/DataWithRange.cs new file mode 100644 index 000000000000..9d2c9f7d4198 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/DataWithRange.cs @@ -0,0 +1,62 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using System; +using System.ServiceModel.Channels; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + public class DataWithRange : IDisposable + { + private readonly BufferManager manager; + private bool disposed; + + public DataWithRange(BufferManager manager) + { + this.manager = manager; + } + + public bool IsAllZero() + { + var startIndex = Array.FindIndex(this.Data, 0, (int)this.Range.Length, b => b != 0); + + return startIndex == -1; + } + + public byte[] Data { get; set; } + public IndexRange Range { get; set; } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (this.disposed) + { + return; + } + + if (disposing) + { + this.manager.ReturnBuffer(this.Data); + this.Data = null; + } + this.disposed = true; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/ExtensionMethods.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/ExtensionMethods.cs new file mode 100644 index 000000000000..6204d703dcec --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/ExtensionMethods.cs @@ -0,0 +1,244 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence; +using Microsoft.WindowsAzure.Storage; +using Microsoft.WindowsAzure.Storage.Blob; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + public static class CloudPageBlobExtensions + { + public static IEnumerable ListContainerBlobs( + this CloudBlobContainer container, + bool useFlatBlobListing, + BlobListingDetails details, + BlobRequestOptions options) + { + BlobContinuationToken continuationToken = null; + string prefix = null; + const int maxBlobsPerRequest = 10; + var blobs = new List(); + do + { + var listingResult = container.ListBlobsSegmentedAsync(prefix, useFlatBlobListing, details, maxBlobsPerRequest, continuationToken, options, null) + .ConfigureAwait(false) + .GetAwaiter().GetResult(); + continuationToken = listingResult.ContinuationToken; + blobs.AddRange(listingResult.Results); + } + while (continuationToken != null); + + return blobs; + } + + public static void SetUploadMetaData(this CloudPageBlob blob, LocalMetaData metaData) + { + if (metaData == null) + { + throw new ArgumentNullException("metaData"); + } + blob.Metadata[LocalMetaData.MetaDataKey] = SerializationUtil.GetSerializedString(metaData); + } + + public static void CleanUpUploadMetaData(this CloudPageBlob blob) + { + blob.Metadata.Remove(LocalMetaData.MetaDataKey); + } + + public static LocalMetaData GetUploadMetaData(this CloudPageBlob blob) + { + return blob.Metadata.Keys.Contains(LocalMetaData.MetaDataKey) + ? SerializationUtil.GetObjectFromSerializedString(blob.Metadata[LocalMetaData.MetaDataKey]) + : null; + } + + public static byte[] GetBlobMd5Hash(this CloudPageBlob blob) + { + blob.FetchAttributesAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + return String.IsNullOrEmpty(blob.Properties.ContentMD5) + ? null + : Convert.FromBase64String(blob.Properties.ContentMD5); + } + + public static byte[] GetBlobMd5Hash(this CloudPageBlob blob, BlobRequestOptions requestOptions) + { + blob.FetchAttributesAsync(new AccessCondition(), requestOptions, operationContext: null) + .ConfigureAwait(false).GetAwaiter().GetResult(); + return String.IsNullOrEmpty(blob.Properties.ContentMD5) + ? null + : Convert.FromBase64String(blob.Properties.ContentMD5); + } + + public static void SetBlobMd5Hash(this CloudPageBlob blob, byte[] md5Hash) + { + var base64String = Convert.ToBase64String(md5Hash); + blob.Properties.ContentMD5 = base64String; + } + + public static void RemoveBlobMd5Hash(this CloudPageBlob blob) + { + blob.Properties.ContentMD5 = null; + } + + public static VhdFooter GetVhdFooter(this CloudPageBlob basePageBlob) + { + var vhdFileFactory = new VhdFileFactory(); + using (var file = vhdFileFactory.Create(basePageBlob.OpenReadAsync().ConfigureAwait(false).GetAwaiter().GetResult())) + { + return file.Footer; + } + } + + public static bool Exists(this CloudPageBlob blob) + { + var listBlobItems = blob.Container.ListContainerBlobs(false, BlobListingDetails.None, null); + var blobToUpload = listBlobItems.FirstOrDefault(b => b.Uri == blob.Uri); + if (blobToUpload is CloudBlockBlob) + { + var message = String.Format(" CsUpload is expecting a page blob, however a block blob was found: '{0}'.", blob.Uri); + throw new InvalidOperationException(message); + } + return blobToUpload != null; + } + + public static bool Exists(this CloudPageBlob blob, BlobRequestOptions options) + { + var listBlobItems = blob.Container.ListContainerBlobs(false, BlobListingDetails.UncommittedBlobs, options); + var blobToUpload = listBlobItems.FirstOrDefault(b => b.Uri == blob.Uri); + if (blobToUpload is CloudBlockBlob) + { + var message = String.Format(" CsUpload is expecting a page blob, however a block blob was found: '{0}'.", blob.Uri); + throw new InvalidOperationException(message); + } + return blobToUpload != null; + } + } + + internal static class StringExtensions + { + public static string ToString(this IEnumerable source, string separator) + { + return "[" + string.Join(",", source.Select(s => s.ToString()).ToArray()) + "]"; + } + } + + public class VhdFilePath + { + public VhdFilePath(string absolutePath, string relativePath) + { + AbsolutePath = absolutePath; + RelativePath = relativePath; + } + + public string AbsolutePath { get; private set; } + public string RelativePath { get; private set; } + } + + internal static class VhdFileExtensions + { + public static IEnumerable GetChildrenIds(this VhdFile vhdFile, Guid uniqueId) + { + var identityChain = vhdFile.GetIdentityChain(); + + if (!identityChain.Contains(uniqueId)) + { + yield break; + } + + foreach (var id in identityChain.TakeWhile(id => id != uniqueId)) + { + yield return id; + } + } + + public static VhdFilePath GetFilePathBy(this VhdFile vhdFile, Guid uniqueId) + { + VhdFilePath result = null; + var newBlocksOwners = new List { Guid.Empty }; + + var current = vhdFile; + while (current != null && current.Footer.UniqueId != uniqueId) + { + newBlocksOwners.Add(current.Footer.UniqueId); + if (current.Parent != null) + { + result = new VhdFilePath(current.Header.GetAbsoluteParentPath(), + current.Header.GetRelativeParentPath()); + } + current = current.Parent; + } + + if (result == null) + { + var message = String.Format("There is no parent VHD file with with the id '{0}'", uniqueId); + throw new InvalidOperationException(message); + } + + return result; + } + } + + public static class UriExtensions + { + /// + /// Normalizes a URI for use as a blob URI. + /// + /// + /// Ensures that the container name is lower-case. + /// + public static Uri NormalizeBlobUri(this Uri uri) + { + var ub = new UriBuilder(uri); + var parts = ub.Path + .Split(new char[] { '/' }, StringSplitOptions.None) + .Select((p, i) => i == 1 ? p.ToLowerInvariant() : p) + .ToArray(); + ub.Path = string.Join("/", parts); + return ub.Uri; + } + + } + + public static class ExceptionUtil + { + public static string DumpStorageExceptionErrorDetails(StorageException storageException) + { + if (storageException == null) + { + return string.Empty; + } + + var message = new StringBuilder(); + message.AppendLine("StorageException details"); +// TODO: Remove IfDef +#if NETSTANDARD + message.Append("Error.Code:").AppendLine(storageException.RequestInformation.ErrorCode); +#else + message.Append("Error.Code:").AppendLine(storageException.RequestInformation.ExtendedErrorInformation.ErrorCode); +#endif + message.Append("ErrorMessage:").AppendLine(storageException.RequestInformation.ExtendedErrorInformation.ErrorMessage); + foreach (var key in storageException.RequestInformation.ExtendedErrorInformation.AdditionalDetails.Keys) + { + message.Append(key).Append(":").Append(storageException.RequestInformation.ExtendedErrorInformation.AdditionalDetails[key]); + } + return message.ToString(); + } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/IndexRangeHelper.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/IndexRangeHelper.cs new file mode 100644 index 000000000000..6295686a00b7 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/IndexRangeHelper.cs @@ -0,0 +1,60 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + internal static class IndexRangeHelper + { + public static IEnumerable ChunkRangesBySize(IEnumerable extents, int pageSizeInBytes) + { + var extentRanges = extents.SelectMany(e => e.PartitionBy(pageSizeInBytes)); + var extentQueue = new Queue(extentRanges); + + if (extentQueue.Count == 0) + { + yield break; + } + + // move to next start position + do + { + var result = extentQueue.Dequeue(); + while (result.Length < pageSizeInBytes && extentQueue.Count > 0) + { + var nextRange = extentQueue.Peek(); + if (!nextRange.Abuts(result)) + { + break; + } + var mergedRange = nextRange.Merge(result); + if (mergedRange.Length <= pageSizeInBytes) + { + result = mergedRange; + extentQueue.Dequeue(); + } + else + { + break; + } + } + yield return result; + } while (extentQueue.Count > 0); + } + + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/PatchingBlobCreator.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/PatchingBlobCreator.cs new file mode 100644 index 000000000000..694bb7124fd6 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/PatchingBlobCreator.cs @@ -0,0 +1,208 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Sync.Download; +using Microsoft.WindowsAzure.Commands.Tools.Vhd; +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence; +using Microsoft.WindowsAzure.Storage.Blob; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + public class PatchingBlobCreator : BlobCreatorBase + { + protected Uri baseVhdBlob; + protected BlobUri baseVhdBlobUri; + + public PatchingBlobCreator(FileInfo localVhd, BlobUri destination, BlobUri baseVhdBlob, ICloudPageBlobObjectFactory blobObjectFactory, bool overWrite) : + base(localVhd, destination, blobObjectFactory, overWrite) + { + this.baseVhdBlob = baseVhdBlob.Uri; + this.baseVhdBlobUri = baseVhdBlob; + } + + + protected override void CreateRemoteBlobAndPopulateContext(UploadContext context) + { + CreateRemoteBlob(); + PopulateContextWithUploadableRanges(localVhd, context, true); + PopulateContextWithDataToUpload(localVhd, context); + } + + private void CreateRemoteBlob() + { + var baseBlob = this.blobObjectFactory.Create(baseVhdBlobUri); + + if (!baseBlob.Exists()) + { + throw new InvalidOperationException(String.Format("Base image to patch doesn't exist in blob storage: {0}", baseVhdBlobUri.Uri)); + } + var blobVhdFooter = baseBlob.GetVhdFooter(); + + long blobSize; + VhdFilePath localBaseVhdPath; + IEnumerable childrenVhdIds; + using (var vhdFile = new VhdFileFactory().Create(localVhd.FullName)) + { + localBaseVhdPath = vhdFile.GetFilePathBy(blobVhdFooter.UniqueId); + childrenVhdIds = vhdFile.GetChildrenIds(blobVhdFooter.UniqueId).ToArray(); + blobSize = vhdFile.Footer.VirtualSize; + } + + FileMetaData fileMetaData = GetFileMetaData(baseBlob, localBaseVhdPath); + + var md5Hash = baseBlob.GetBlobMd5Hash(); + if (!md5Hash.SequenceEqual(fileMetaData.MD5Hash)) + { + var message = String.Format("Patching cannot proceed, MD5 hash of base image in blob storage ({0}) and base VHD file ({1}) does not match ", + baseBlob.Uri, + localBaseVhdPath); + throw new InvalidOperationException(message); + } + + Program.SyncOutput.MessageCreatingNewPageBlob(blobSize); + + CopyBaseImageToDestination(); + + using (var vds = new VirtualDiskStream(localVhd.FullName)) + { + var streamExtents = vds.Extents.ToArray(); + var enumerable = streamExtents.Where(e => childrenVhdIds.Contains(e.Owner)).ToArray(); + foreach (var streamExtent in enumerable) + { + var indexRange = streamExtent.Range; + destinationBlob.ClearPagesAsync(indexRange.StartIndex, indexRange.Length) + .ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + + using (var bmds = new BlobMetaDataScope(destinationBlob)) + { + bmds.Current.RemoveBlobMd5Hash(); + bmds.Current.SetUploadMetaData(OperationMetaData); + bmds.Complete(); + } + } + + private void CopyBaseImageToDestination() + { + var source = this.blobObjectFactory.Create(baseVhdBlobUri); + source.FetchAttributesAsync() + .ConfigureAwait(false).GetAwaiter().GetResult(); + + var copyStatus = new ProgressStatus(0, source.Properties.Length); + using (new ProgressTracker(copyStatus, Program.SyncOutput.ProgressCopyStatus, Program.SyncOutput.ProgressCopyComplete, TimeSpan.FromSeconds(1))) + { + destinationBlob.StartCopyAsync(source).ConfigureAwait(false).GetAwaiter().GetResult(); + destinationBlob.FetchAttributesAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + + while (true) + { + if (destinationBlob.CopyState.BytesCopied != null) + { + copyStatus.AddToProcessedBytes(destinationBlob.CopyState.BytesCopied.Value - copyStatus.BytesProcessed); + } + if (destinationBlob.CopyState.Status == CopyStatus.Success) + { + break; + } + if (destinationBlob.CopyState.Status == CopyStatus.Pending) + { + Thread.Sleep(TimeSpan.FromSeconds(1)); + } + else + { + throw new ApplicationException( + string.Format("Cannot copy source '{0}' to destination '{1}', copy state is '{2}'", source.Uri, + destinationBlob.Uri, destinationBlob.CopyState)); + } + destinationBlob.FetchAttributesAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + } + + private FileMetaData GetFileMetaData(CloudPageBlob baseBlob, VhdFilePath localBaseVhdPath) + { + FileMetaData fileMetaData; + if (File.Exists(localBaseVhdPath.AbsolutePath)) + { + fileMetaData = FileMetaData.Create(localBaseVhdPath.AbsolutePath); + } + else + { + var filePath = Path.Combine(localVhd.Directory.FullName, localBaseVhdPath.RelativePath); + if (File.Exists(filePath)) + { + fileMetaData = FileMetaData.Create(filePath); + } + else + { + var message = String.Format("Cannot find the local base image for '{0}' in neither of the locations '{1}', '{2}'.", + baseBlob.Uri, + localBaseVhdPath.AbsolutePath, + localBaseVhdPath.RelativePath); + throw new InvalidOperationException(message); + } + } + return fileMetaData; + } + } + + class BlobMetaDataScope : IDisposable + { + private readonly CloudPageBlob blob; + private bool disposed; + private bool completed; + + public BlobMetaDataScope(CloudPageBlob blob) + { + this.blob = blob; + this.blob.FetchAttributesAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + this.Current = blob; + } + + public CloudPageBlob Current { get; private set; } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected void Dispose(bool disposing) + { + if (disposing) + { + if (!this.disposed) + { + if (completed) + { + this.blob.SetMetadataAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + this.blob.SetPropertiesAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + } + this.disposed = true; + } + } + } + + public void Complete() + { + this.completed = true; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/SerializationUtil.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/SerializationUtil.cs new file mode 100644 index 000000000000..0964fdcc02ae --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/SerializationUtil.cs @@ -0,0 +1,46 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System.IO; +using System.Runtime.Serialization; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + internal class SerializationUtil + { + public static T GetObjectFromSerializedString(string data) + { + var serializer = new DataContractSerializer(typeof(T)); + using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(data))) + { + return (T)serializer.ReadObject(memoryStream); + } + } + + public static string GetSerializedString(T localMetaData) + { + var serializer = new DataContractSerializer(typeof(T)); + string serializedString; + using (var memoryStream = new MemoryStream()) + { + serializer.WriteObject(memoryStream, localMetaData); + memoryStream.Flush(); + byte[] bytes = memoryStream.ToArray(); + serializedString = Encoding.UTF8.GetString(bytes, 0, bytes.Length); + } + return serializedString; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/UploadContext.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/UploadContext.cs new file mode 100644 index 000000000000..6837e7e1c07a --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/UploadContext.cs @@ -0,0 +1,66 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using Microsoft.WindowsAzure.Storage.Blob; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + public class UploadContext : IDisposable + { + private bool disposed; + + public CloudPageBlob DestinationBlob { get; set; } + + public IEnumerable UploadableDataWithRanges { get; set; } + + public IEnumerable UploadableRanges { get; set; } + + public long UploadableDataSize { get; set; } + + public long AlreadyUploadedDataSize { get; set; } + + public byte[] Md5HashOfLocalVhd { get; set; } + + public Mutex SingleInstanceMutex { get; set; } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + { + return; + } + + if (disposing) + { + if (SingleInstanceMutex != null) + { + SingleInstanceMutex.ReleaseMutex(); + SingleInstanceMutex.Close(); + } + + disposed = true; + } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/UploadOperationMetaData.cs b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/UploadOperationMetaData.cs new file mode 100644 index 000000000000..1c6bd7844e33 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/Sync/Upload/UploadOperationMetaData.cs @@ -0,0 +1,158 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + + +using Microsoft.WindowsAzure.Commands.Sync.IO; +using Microsoft.WindowsAzure.Commands.Tools.Vhd; +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.Serialization; +using System.Security.Cryptography; +using System.Security.Permissions; + +namespace Microsoft.WindowsAzure.Commands.Sync.Upload +{ + [DataContract] + public class FileMetaData + { + [DataMember(EmitDefaultValue = false, Order = 10)] + public string FileFullName { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 20)] + private string InternalCreatedDateUtc { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 30)] + private string InternalLastModifiedDateUtc { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 40)] + public long Size { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 50)] + public long VhdSize { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 60)] + public byte[] MD5Hash { get; set; } + + public DateTime CreatedDateUtc + { + get { return DateTime.Parse(this.InternalCreatedDateUtc, DateTimeFormatInfo.InvariantInfo); } + set { this.InternalCreatedDateUtc = value.ToString(DateTimeFormatInfo.InvariantInfo); } + } + + public DateTime LastModifiedDateUtc + { + get { return DateTime.Parse(this.InternalLastModifiedDateUtc, DateTimeFormatInfo.InvariantInfo); } + set { this.InternalLastModifiedDateUtc = value.ToString(DateTimeFormatInfo.InvariantInfo); } + } + + public static FileMetaData Create(string filePath) + { + var fileInfo = new FileInfo(filePath); + if (!fileInfo.Exists) + { + throw new FileNotFoundException(filePath); + } + + using (var stream = new VirtualDiskStream(filePath)) + { + return new FileMetaData + { + FileFullName = fileInfo.FullName, + CreatedDateUtc = DateTime.SpecifyKind(fileInfo.CreationTimeUtc, DateTimeKind.Utc), + LastModifiedDateUtc = DateTime.SpecifyKind(fileInfo.LastWriteTimeUtc, DateTimeKind.Utc), + Size = fileInfo.Length, + VhdSize = stream.Length, + MD5Hash = CalculateMd5Hash(stream, filePath) + }; + } + } + + private static byte[] CalculateMd5Hash(Stream stream, string filePath) + { + using (var md5 = MD5.Create()) + { + using (var swrp = new StreamWithReadProgress(stream, TimeSpan.FromSeconds(1))) + { + var bs = new BufferedStream(swrp); + Program.SyncOutput.MessageCalculatingMD5Hash(filePath); + var md5Hash = md5.ComputeHash(bs); + Program.SyncOutput.MessageMD5HashCalculationFinished(); + return md5Hash; + } + } + } + } + + [DataContract] + public class SystemInformation + { + [DataMember(EmitDefaultValue = false, Order = 10)] + public string MachineName { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 20)] + public int CsUploadProcessId { get; set; } + + [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] + public static SystemInformation Create() + { + return new SystemInformation + { + MachineName = Environment.MachineName, + CsUploadProcessId = Process.GetCurrentProcess().Id + }; + } + } + + + [DataContract] + public class LocalMetaData + { + public static string MetaDataKey = "localmetadata"; + + [DataMember(EmitDefaultValue = false, Order = 10)] + public FileMetaData FileMetaData { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 20)] + public SystemInformation SystemInformation { get; set; } + } + + [DataContract] + public class LeaseMetaData + { + public static string MetaDataKey = "leasemetadata"; + + [DataMember(EmitDefaultValue = false, Order = 10)] + public Guid LeaseId { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 20)] + public DateTime LeaseExpirationDateUtc { get; set; } + } + + [DataContract] + public class BlobMetaData + { + public static string MetaDataKey = "blobmetadata"; + + [DataMember(EmitDefaultValue = false, Order = 10)] + public string ETag { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 20)] + public DateTime LastModifiedUtc { get; set; } + + [DataMember(EmitDefaultValue = false, Order = 30)] + public byte[] MD5Hash { get; set; } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Async/AsyncMachine.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Async/AsyncMachine.cs new file mode 100644 index 000000000000..1095a4a2a9ea --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Async/AsyncMachine.cs @@ -0,0 +1,1148 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Threading; + +namespace Microsoft.WindowsAzure.Commands.Tools.Common.General +{ + // + // The asynchronous machine below supports two modes, either all operations are required to complete or + // only a subset of them (quorum) is required to proceed. There is also a completion port optimization + // for a single asynchronous operation (CompletionPort.SingleOperation) to avoid unnecessary memory allocations. + // + // Examples: + // + // Scenario 1: Single asynchronous operation without an explicit timeout + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // + // ... + // AsyncMachine machine = new AsyncMachine(); + // machine.Start(this.ProcessMessage(asyncMachine, message)); + // ... + // + // IEnumerable ProcessMessage(AsyncMachine machine, Message message) + // { + // ... + // WebRequest request = WebRequest.Create(); + // + // request.BeginGetResponse(machine.CompletionCallback, null); + // + // yield return CompletionPort.SingleOperation; + // + // ProcessWebResponse(request, machine.CompletionResult); + // ... + // + // + // Scenario 2: Multiple asynchronous operations with time out + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // + // IEnumerable ProcessMessage(AsyncMachine machine, Message message) + // { + // ... + // CompletionPort port = machine.CreateCompletionPort(TimeSpan.FromSeconds(10)); + // + // foreach (WebRequest request in pendingRequests) + // { + // request.BeginGetResponse(port[request].CompletionCallback, null); + // } + // + // yield return port; + // + // foreach (WebRequest request in pendingRequests) + // { + // // The operation is not completed if it's timed out + // if (port[request].IsCompleted) + // { + // ProcessWebResponse(request, port[request].CompletionResult); + // } + // else + // { + // port[request].Cancel(request.EndGetResponse); + // } + // } + // ... + // + // + // Scenario 3: Multiple asynchronous operations with quorum and time out + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // + // IEnumerable ProcessMessage(AsyncMachine machine, Message message) + // { + // ... + // + // // The async machine will resume if 3 operations out of 5 will complete + // CompletionPort port = machine.CreateCompletionPort(5, 3, TimeSpan.FromSeconds(10)); + // + // for (int c = 0; c < 5; ++c) + // { + // WebRequest request = pendingRequests[c]; + // + // request.BeginGetResponse(port[request].CompletionCallback, null); + // } + // + // yield return port; + // + // for (int c = 0; c < 5; ++c) + // { + // WebRequest request = pendingRequests[c]; + // + // // The operation is not completed if it's timed out + // if (port[request].IsCompleted) + // { + // ProcessWebResponse(request, port[request].CompletionResult); + // } + // else + // { + // port[request].Cancel(request.EndGetResponse); + // } + // } + // ... + // + + // Represents an asynchronous operation + public class AsyncOperation + { + #region Constructors + + internal AsyncOperation(Action checkOperationCompletion) + { + Debug.Assert(checkOperationCompletion != null); + + this.CheckOperationCompletion = checkOperationCompletion; + this.CompletionCallback = this.AsyncCallback; + } + + #endregion + + public AsyncCallback CompletionCallback { get; private set; } + + public IAsyncResult CompletionResult + { + get + { + if (this.completionResult == null) + { + throw new InvalidOperationException("Completion result is not available yet."); + } + + return this.completionResult; + } + } + + // Indicates whether a particular asynchronous operation is completed + public bool IsCompleted { get; internal set; } + + // Cancels pending async operation meaning that we'll call the corresponding end operation whenever it completes + // (since there is no currently real cancelation mechanism for .NET async operations) + public void Cancel(AsyncCallback endAsyncOperation) + { + if (endAsyncOperation == null) + { + throw new ArgumentNullException("endAsyncOperation"); + } + + this.EndAsynchronousOperation = endAsyncOperation; + + if (this.CompletionResult != null && Interlocked.CompareExchange(ref this.isEndOperationCalled, True, False) == False) + { + // The operation is completed but end async operation is not called yet, need to call it + this.EndAsynchronousOperation(this.CompletionResult); + } + } + + #region Private Members + + // Gets invoked when an asynchronous operation has completed + private void AsyncCallback(IAsyncResult result) + { + if (result == null) + { + throw new ArgumentNullException("result"); + } + + this.completionResult = result; + + // this.CheckOperationCompletion will set AsyncOperation.IsCompleted to false if quorum is reached or timeout is expired, + // and async machine has resumed its execution. In this case we'll consider the operation not completed in time and + // require user to schedule cancelation on completion port + this.CheckOperationCompletion(this); + + if (!this.IsCompleted && this.EndAsynchronousOperation != null && Interlocked.CompareExchange(ref this.isEndOperationCalled, True, False) == False) + { + // Call an end of asynchronous operation method if callback above returned false, this means + // it's us who is responsible for calling the end of asynchronous operation + this.EndAsynchronousOperation(this.CompletionResult); + } + } + + private Action CheckOperationCompletion { get; set; } + private AsyncCallback EndAsynchronousOperation { get; set; } + private IAsyncResult completionResult; + private int isEndOperationCalled; + + const int True = -1; + const int False = 0; + + #endregion + } + + // Defines a completion port which keeps track of asynchronous operations which can be run concurrently + // and results of their execution + public class CompletionPort + { + #region Constructors + + internal CompletionPort( + Action callback, + int totalOperationsCount, + int quorumOperationsCount, + TimeSpan timeout + ) + { + if (callback == null) + { + throw new ArgumentNullException("callback"); + } + + if (quorumOperationsCount > totalOperationsCount) + { + throw new ArgumentException("Invalid quorum operations count."); + } + + this.AsyncMachineResume = callback; + this.TotalOperationsCount = totalOperationsCount; + this.QuorumOperationsCount = quorumOperationsCount; + this.Timeout = timeout; + + this.OperationsLock = new object(); + this.Operations = new Dictionary(totalOperationsCount); + } + + internal CompletionPort(bool isSingleOperation) + { + Debug.Assert(isSingleOperation); + + this.IsSingleOperation = isSingleOperation; + } + + #endregion + + // Gets an asynchronous operation instance to be used for a given actor + public AsyncOperation this[object actor] + { + get + { + if (this.Operations == null) + { + throw new InvalidOperationException("CompletionPort.SingleOperation is not mutable."); + } + + AsyncOperation operation; + + lock (this.OperationsLock) + { + if (!this.Operations.TryGetValue(actor, out operation)) + { + // Check if async machine resume callback is already called, this means that + // this completion port is being reused which we don't allow + if (this.asyncMachineResumed == True) + { + throw new InvalidOperationException("Attempt to reuse a completion port."); + } + + operation = new AsyncOperation(this.CheckOperationCompletion); + this.Operations.Add(actor, operation); + } + } + + return operation; + } + } + + // Total number of operations in this completion token + public int TotalOperationsCount { get; private set; } + + // Number of operations to treat as a success to advance + public int QuorumOperationsCount { get; private set; } + + // Timeout indicating when wait on this completion port expires + public TimeSpan Timeout { get; private set; } + + // Provides a completion port to be used for single asynchronous operations + public static CompletionPort SingleOperation { get; private set; } + + #region Internal Members + + // Called by the async machine when this port is timed out and all operations have to marked as such + internal void TimeOutPort() + { + if (Interlocked.CompareExchange(ref this.asyncMachineResumed, True, False) == False) + { + // + // Async machine is not resumed at this point despite that some operations may be completed by this time. + // We'll mark all operations as incomplete and resume async machine execution so that user would schedule + // cancelation on all port operations. + // + + lock (this.OperationsLock) + { + foreach (var pair in this.Operations) + { + pair.Value.IsCompleted = false; + } + } + + Debug.Assert(this.AsyncMachineResume != null); + + this.AsyncMachineResume(); + } + } + + internal bool IsSingleOperation { get; private set; } + internal static readonly TimeSpan InfiniteWait = new TimeSpan(0, 0, 0, 0, -1); + + #endregion + + #region Private Members + + // Creates a singleton instance of completion port which can be used when async machine runs single + // asynchronous operations + static CompletionPort() + { + CompletionPort.SingleOperation = new CompletionPort(true); + } + + // Invoked by a completed asynchronous operation + private void CheckOperationCompletion(AsyncOperation operation) + { + // Mark async operation as completed if async machine is not resumed yet + operation.IsCompleted = this.asyncMachineResumed == False; + + if (operation.IsCompleted) + { + // + // Check if all or quorum (in case we're in quorum mode) number of operations have completed, + // in this case we need to notify the state machine that it can resume + // + + Debug.Assert(this.QuorumOperationsCount > 0); + + if (Interlocked.Increment(ref this.completedOperations) >= this.QuorumOperationsCount && + Interlocked.CompareExchange(ref this.asyncMachineResumed, True, False) == False) + { + Debug.Assert(this.AsyncMachineResume != null); + + this.AsyncMachineResume(); + } + } + } + + private Action AsyncMachineResume { get; set; } + private Dictionary Operations { get; set; } + private object OperationsLock { get; set; } + private int asyncMachineResumed; + private int completedOperations; + + const int True = -1; + const int False = 0; + + #endregion + } + + public delegate IEnumerable MachineEngine(AsyncMachine machine); + public delegate IEnumerable MachineEngine

(AsyncMachine machine, P param); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2, P3 param3); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10); + public delegate IEnumerable MachineEngine(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10, P11 param11); + + // General purpose async machine which supports single (optimized) and multiple + // asynchronous operations + public class AsyncMachine : IAsyncResult, IDisposable + { + #region Helper static methods + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine

(MachineEngine

engine, P param, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, P3 param3, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, P3 param3, P4 param4, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8, param9)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngine engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10, P11 param11, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11)); + return machine; + } + + + public static void EndAsyncMachine(IAsyncResult result) + { + using (AsyncMachine machine = (AsyncMachine)result) + { + machine.End(); + } + } + + #endregion + + #region Constructors + + public AsyncMachine(AsyncCallback callback, object state) + { + this.asyncMachineCompletionCallback = callback; + this.AsyncState = state; + + this.CompletionCallback = this.SingleOperationCompletionCallback; + this.moveNextLock = new object(); + } + + #endregion + + + // Create completion port with infinite timeout + public CompletionPort CreateCompletionPort(int totalOperationsCount) + { + return CreateCompletionPort(totalOperationsCount, CompletionPort.InfiniteWait); + } + + // Create completion port with specified timeout + public CompletionPort CreateCompletionPort(int totalOperationsCount, TimeSpan timeout) + { + return CreateCompletionPort(totalOperationsCount, totalOperationsCount, timeout); + } + + // Creates completion port in the quorum mode with a specified timeout (you can use CompletionPort.InfiniteWait + // for infinite timeout) + public CompletionPort CreateCompletionPort(int totalOperationsCount, int quorumOperationsCount) + { + return CreateCompletionPort( + totalOperationsCount, + quorumOperationsCount, + CompletionPort.InfiniteWait); + } + + // Creates completion port in the quorum mode with a specified timeout (you can use CompletionPort.InfiniteWait + // for infinite timeout) + public CompletionPort CreateCompletionPort(int totalOperationsCount, int quorumOperationsCount, TimeSpan timeout) + { + if (this.Enumerator == null) + { + throw new InvalidOperationException("Async machine either hasn't started yet or has already completed."); + } + + return new CompletionPort( + this.AsyncMachineResumeCallback, + totalOperationsCount, + quorumOperationsCount, + timeout); + } + + public delegate void ExceptionCleanupDelegate(object sender, EventArgs e); + + // Event which is fired when exception cleanup is required + public event ExceptionCleanupDelegate ExceptionCleanup; + + // Starts executing of the async machine + public void Start(IEnumerable machine) + { + Debug.Assert(null == this.Enumerator || this.IsCompleted); + + if (null == machine) + { + throw new ArgumentNullException("machine"); + } + + this.Enumerator = machine.GetEnumerator(); + this.IsCompleted = false; + this.Error = null; + + if (this.waitHandle != null) + { + this.waitHandle.Reset(); + } + + lock (this.moveNextLock) + { + this.CompletedSynchronously = true; + + this.MoveNext(); + + if (!this.IsCompleted) + { + this.CompletedSynchronously = false; + } + } + } + + // Ends the async machine and waits for its completion if necessary + public void End() + { + if (!this.IsCompleted) + { + this.AsyncWaitHandle.WaitOne(); + } + + Debug.Assert(this.IsCompleted); + + if (this.Error != null) + { + throw this.Error.PrepareServerStackForRethrow(); + } + } + + // A callback supplied to a single asynchronous operation to get notified when it's completed + public AsyncCallback CompletionCallback { get; private set; } + + // A result of completed single asynchronous operation when AsyncMachine.CompletionCallback is used + public IAsyncResult CompletionResult + { + get + { + if (this.completionResult == null) + { + throw new InvalidOperationException("Completion result is not available yet."); + } + + return this.completionResult; + } + } + + #region IAsyncResult Members + + public object AsyncState { get; private set; } + + public WaitHandle AsyncWaitHandle + { + get + { + if (null == this.waitHandle) + { + lock (this.moveNextLock) + { + if (null == this.waitHandle) + { + this.waitHandle = new EventWaitHandle(this.IsCompleted, EventResetMode.ManualReset); + } + } + } + + return this.waitHandle; + } + } + + public bool CompletedSynchronously { get; private set; } + + public bool IsCompleted { get; private set; } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + if (isDisposing) + { + if (this.waitHandle != null) + { + this.waitHandle.Close(); + this.waitHandle = null; + } + + if (this.WakeUpTimer != null) + { + this.WakeUpTimer.Dispose(); + this.WakeUpTimer = null; + } + } + } + + #endregion + + #region Private Members + + private void MoveNext() + { + lock (this.moveNextLock) + { + using (this.EnterThreadContext()) + { + this.MoveNextInternal(); + } + } + } + + private void MoveNextInternal() + { + Debug.Assert(this.Enumerator != null); + Debug.Assert(!this.IsCompleted); + Debug.Assert(null == this.Error); + + if (!this.InsideMoveNext) + { + try + { + try + { + this.InsideMoveNext = true; + this.IsCompleted = !this.Enumerator.MoveNext(); + + if (!this.IsCompleted) + { + if (this.Enumerator.Current == null) + { + throw new InvalidOperationException("Completion port for current iteration is null."); + } + + if (!this.Enumerator.Current.IsSingleOperation) + { + if (this.Enumerator.Current.TotalOperationsCount == 0) + { + // No operations are specified on the port, do another machine iteration + this.InsideMoveNext = false; + this.MoveNext(); + return; + } + else + { + // Check if we should schedule a wake up for the current completion port. That needs to happen + // when we use non-optimized completion port with a timeout + if (!this.Enumerator.Current.Timeout.Equals(CompletionPort.InfiniteWait)) + { + if (this.WakeUpTimer != null) + { + this.WakeUpTimer.Dispose(); + } + + // + // BUGBUG: Too expensive. Use single active task to implement timers + // + this.WakeUpTimer = new Timer( + this.CompletionPortTimeoutCallback, + this.Enumerator.Current, + this.Enumerator.Current.Timeout, + CompletionPort.InfiniteWait); + } + } + } + } + } + finally + { + this.InsideMoveNext = false; + } + } + catch (Exception e) + { + if (e.IsCritical()) + { + Trace.TraceError("Failed to forward message: {0}", e); + throw; + } + + Trace.TraceWarning("Failed to forward message: {0}", e); + + this.Error = e; + this.IsCompleted = true; + + if (this.ExceptionCleanup != null) + { + this.ExceptionCleanup(this, EventArgs.Empty); + } + } + + this.CompletedMoveNext(); + + if (this.AsyncMethodCompletedSynchronously && !this.IsCompleted) + { + this.AsyncMethodCompletedSynchronously = false; + + this.MoveNext(); + } + } + else + { + // + // Call to asynchronous I/O was completed synchronously (i.e. this thread is currently executing MoveNext down the stack) + // + + Debug.Assert(!this.AsyncMethodCompletedSynchronously); + + this.AsyncMethodCompletedSynchronously = true; + } + } + + private void CompletedMoveNext() + { + if (this.IsCompleted) + { + this.Enumerator = null; + + if (null != this.waitHandle) + { + this.waitHandle.Set(); + } + + if (this.asyncMachineCompletionCallback != null) + { + this.asyncMachineCompletionCallback((IAsyncResult)this); + } + } + } + + // Called by the WakeUpTimer when a scheduled timeout wait for the given completion + // port is expired + private void CompletionPortTimeoutCallback(object state) + { + Debug.Assert(state != null); + + CompletionPort port = (CompletionPort)state; + + port.TimeOutPort(); + } + + // Called by the completion port when quorum of asynchronous operations are completed + private void AsyncMachineResumeCallback() + { + // Check if quorum was reached with all operations finishing synchronously + if (!this.InsideMoveNext) + { + IEnumerator enumerator = this.Enumerator; + + // It should never happen that this callback is called for CompletionPort with single operation + if (enumerator != null && enumerator.Current != null && enumerator.Current.IsSingleOperation) + { + throw new InvalidOperationException("CompletionPort.SingleOperation was used."); + } + } + + this.MoveNext(); + } + + // Called by the completed single asynchronous operation + private void SingleOperationCompletionCallback(IAsyncResult asyncResult) + { + if (asyncResult == null) + { + throw new ArgumentNullException("asyncResult"); + } + + Debug.Assert(this.InsideMoveNext || this.Enumerator != null); + Debug.Assert(this.InsideMoveNext || this.Enumerator.Current != null); + + this.completionResult = asyncResult; + + this.MoveNext(); + } + + #region Thread Context helpers + + private IDisposable EnterThreadContext() + { + return null; + } + #endregion + + private IEnumerator Enumerator { get; set; } + private Exception Error { get; set; } + private bool InsideMoveNext { get; set; } + private bool AsyncMethodCompletedSynchronously { get; set; } + private Timer WakeUpTimer { get; set; } + private EventWaitHandle waitHandle; + private IAsyncResult completionResult; + private readonly AsyncCallback asyncMachineCompletionCallback; + private readonly object moveNextLock; + #endregion + } + + #region Parameterized async machines + + public delegate IEnumerable MachineEngineT(AsyncMachine machine); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P param); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2, P3 param3); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10); + public delegate IEnumerable MachineEngineT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10, P11 param11); + + public sealed class AsyncMachine : AsyncMachine + { + #region Helper static methods + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine

(MachineEngineT engine, P param, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, P3 param3, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, P3 param3, P4 param4, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8, param9)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10, P11 param11, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11)); + return machine; + } + + public new static T EndAsyncMachine(IAsyncResult result) + { + using (AsyncMachine machine = (AsyncMachine)result) + { + machine.End(); + return machine.ParameterValue; + } + } + + #endregion + + public AsyncMachine() + : this(null, null) + { + } + + public AsyncMachine(AsyncCallback callback, object state) + : base(callback, state) + { + } + + public T ParameterValue { get; set; } + } + + public delegate IEnumerable MachineEngineRT(AsyncMachine machine); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P param); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2, P3 param3); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10); + public delegate IEnumerable MachineEngineRT(AsyncMachine machine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10, P11 param11); + + public sealed class AsyncMachine : AsyncMachine + { + #region Helper static methods + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine

(MachineEngineRT engine, P param, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, P3 param3, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, P3 param3, P4 param4, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8, param9)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10)); + return machine; + } + + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "EndAsyncMachine does the disposing")] + public static IAsyncResult BeginAsyncMachine(MachineEngineRT engine, P1 param1, P2 param2, P3 param3, P4 param4, P5 param5, P6 param6, P7 param7, P8 param8, P9 param9, P10 param10, P11 param11, AsyncCallback callback, object asyncState) + { + AsyncMachine machine = new AsyncMachine(callback, asyncState); + machine.Start(engine(machine, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10, param11)); + return machine; + } + + public static TReturn EndAsyncMachine(IAsyncResult result, out T parameterValue) + { + using (AsyncMachine machine = (AsyncMachine)result) + { + machine.End(); + parameterValue = machine.ParameterValue; + return machine.ReturnValue; + } + } + + #endregion + + public AsyncMachine() + : this(null, null) + { + } + + public AsyncMachine(AsyncCallback callback, object state) + : base(callback, state) + { + } + + public TReturn ReturnValue { get; set; } + public T ParameterValue { get; set; } + } + + #endregion +} diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Async/ExceptionExtension.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Async/ExceptionExtension.cs new file mode 100644 index 000000000000..9d9905e3eac1 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Async/ExceptionExtension.cs @@ -0,0 +1,59 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Microsoft.WindowsAzure.Commands.Tools.Common.General +{ + public static class ExceptionExtensions + { + private static readonly MethodInfo methodPrepForRemoting = typeof(Exception).GetMethod("PrepForRemoting", BindingFlags.NonPublic | BindingFlags.Instance); + + public static bool IsCritical( + this Exception exception + ) + { + Debug.Assert(null != exception); + + if (null == exception) + { + throw new ArgumentNullException("exception"); + } + + return ( + (exception is AccessViolationException) || + (exception is InsufficientMemoryException) || + (exception is OutOfMemoryException) || + (exception is SEHException) || + (exception is StackOverflowException) || + (exception is ThreadAbortException) + ); + } + + public static Exception PrepareServerStackForRethrow( + this Exception exception + ) + { + Debug.Assert(null != exception); + + methodPrepForRemoting.Invoke(exception, new object[0]); + + return exception; + } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/AttributeHelper.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/AttributeHelper.cs new file mode 100644 index 000000000000..967992db0a2a --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/AttributeHelper.cs @@ -0,0 +1,94 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class AttributeHelper + { + private readonly Type type; + public AttributeHelper() + { + type = typeof(T); + } + + public VhdEntityAttribute GetEntityAttribute() + { + var attributes = type.GetCustomAttributes(typeof(VhdEntityAttribute), false); + if (attributes.Count() == 0) + { + throw new InvalidOperationException(String.Format("Entity must have the attribute:{0}", typeof(VhdEntityAttribute).Name)); + } + return (VhdEntityAttribute)attributes.ElementAt(0); + } + + public VhdPropertyAttribute GetAttribute(Expression> propertyNameProvider) + { + MemberExpression me; + if (propertyNameProvider.Body is UnaryExpression) + { + me = ((UnaryExpression)propertyNameProvider.Body).Operand as MemberExpression; + } + else + { + me = propertyNameProvider.Body as MemberExpression; + } + + if (me == null || me.Expression.Type != type) + { + throw new InvalidOperationException("Not a valid expression, must be a VhdFooter property access"); + } + + var propertyName = me.Member.Name; + + var attributes = from p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public) + let vhdPropertyAttributes = p.GetCustomAttributes(typeof(VhdPropertyAttribute), false) + let exists = vhdPropertyAttributes.Count() > 0 + where p.Name == propertyName + select (VhdPropertyAttribute)(vhdPropertyAttributes.ElementAt(0)); + return attributes.FirstOrDefault(); + } + + public VhdPropertyAttribute GetAttribute(Expression> propertyNameProvider) + { + MemberExpression me; + if (propertyNameProvider.Body is UnaryExpression) + { + me = ((UnaryExpression)propertyNameProvider.Body).Operand as MemberExpression; + } + else + { + me = propertyNameProvider.Body as MemberExpression; + } + + if (me == null || me.Expression.Type != type) + { + throw new InvalidOperationException("Not a valid expression, must be a VhdFooter property access"); + } + + var propertyName = me.Member.Name; + + var attributes = from p in type.GetProperties(BindingFlags.Instance | BindingFlags.Public) + let vhdPropertyAttributes = p.GetCustomAttributes(typeof(VhdPropertyAttribute), false) + let exists = vhdPropertyAttributes.Count() > 0 + where p.Name == propertyName + select (VhdPropertyAttribute)(vhdPropertyAttributes.ElementAt(0)); + return attributes.FirstOrDefault(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/BitMap.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/BitMap.cs new file mode 100644 index 000000000000..109f5cf0077e --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/BitMap.cs @@ -0,0 +1,38 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System.Collections; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class BitMap + { + public BitMap(BitArray data) + { + this.Data = data; + Covered = CheckIfCovered(); + } + + public BitArray Data { get; private set; } + + public bool Covered { get; private set; } + + bool CheckIfCovered() + { + for (int i = 0; i < Data.Length; i++) + if (!Data[i]) return false; + return true; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Block.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Block.cs new file mode 100644 index 000000000000..d7c7c94dd7a8 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Block.cs @@ -0,0 +1,65 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence; +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class Block + { + private readonly IBlockFactory blockFactory; + private byte[] data; + + public Block(IBlockFactory blockFactory) + { + this.blockFactory = blockFactory; + } + + public Guid VhdUniqueId { get; set; } + public uint BlockIndex { get; set; } + public BitMap BitMap { get; set; } + + public byte[] Data + { + get + { + if (data == null) + { + data = this.blockFactory.ReadBlockData(this); + } + return data; + } + } + + public long SectorCount + { + get { return this.LogicalRange.Length / VhdConstants.VHD_SECTOR_LENGTH; } + } + + public Sector GetSector(uint sector) + { + return this.blockFactory.GetSector(this, sector); + } + + public bool Empty { get; set; } + + public IndexRange LogicalRange { get; set; } + + public override string ToString() + { + return BlockIndex.ToString(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/BlockAllocationTable.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/BlockAllocationTable.cs new file mode 100644 index 000000000000..36bafa55afa8 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/BlockAllocationTable.cs @@ -0,0 +1,61 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence; +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class BlockAllocationTable + { + private readonly uint blockSize; + + public BlockAllocationTable(uint size, uint blockSize, uint[] bat) + { + this.blockSize = blockSize; + this.Size = size; + this.Data = bat; + } + + public uint Size { get; internal set; } + + private uint[] Data { get; set; } + + public long GetBlockAddress(uint block) + { + return GetBitMapAddress(block) + GetSectorPaddedBitmapSizeInBytes(); + } + + public long GetBitMapAddress(uint block) + { + return ((long)this.Data[block]) * VhdConstants.VHD_SECTOR_LENGTH; + } + + public int GetSectorPaddedBitmapSizeInBytes() + { + var sectorSpanOfBitMap = (double)GetBitmapSizeInBytes() / VhdConstants.VHD_SECTOR_LENGTH; + return (int)(Math.Ceiling(sectorSpanOfBitMap) * VhdConstants.VHD_SECTOR_LENGTH); + } + + public int GetBitmapSizeInBytes() + { + return (int)(blockSize / VhdConstants.VHD_SECTOR_LENGTH / 8); + } + + public bool HasData(uint block) + { + return block != VhdConstants.VHD_NO_DATA_INT && Data[block] != VhdConstants.VHD_NO_DATA_INT; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/DiskGeometry.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/DiskGeometry.cs new file mode 100644 index 000000000000..517b055ed62a --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/DiskGeometry.cs @@ -0,0 +1,122 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence; +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + [VhdEntity(Size = 4)] + public class DiskGeometry + { + public static DiskGeometry CreateFromVirtualSize(long size) + { + long totalSectors = size / VhdConstants.VHD_SECTOR_LENGTH; + if (totalSectors > 65535 * 16 * 255) + { + totalSectors = 65535 * 16 * 255; + } + + int sectorsPerTrack; + int heads; + long cylinderTimesHeads; + if (totalSectors >= 65535 * 16 * 63) + { + sectorsPerTrack = 255; + heads = 16; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + } + else + { + sectorsPerTrack = 17; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + + heads = (int)((cylinderTimesHeads + 1023) / 1024); + + if (heads < 4) + { + heads = 4; + } + if (cylinderTimesHeads >= (heads * 1024) || heads > 16) + { + sectorsPerTrack = 31; + heads = 16; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + } + if (cylinderTimesHeads >= (heads * 1024)) + { + sectorsPerTrack = 63; + heads = 16; + cylinderTimesHeads = totalSectors / sectorsPerTrack; + } + } + long cylinders = cylinderTimesHeads / heads; + + return new DiskGeometry + { + Cylinder = (short)cylinders, + Heads = (byte)heads, + Sectors = (byte)sectorsPerTrack + }; + } + + [VhdProperty(Offset = 0, Size = 2)] + public short Cylinder { get; set; } + + [VhdProperty(Offset = 2, Size = 1)] + public byte Heads { get; set; } + + [VhdProperty(Offset = 3, Size = 1)] + public byte Sectors { get; set; } + + public DiskGeometry CreateCopy() + { + return new DiskGeometry + { + Cylinder = this.Cylinder, + Heads = this.Heads, + Sectors = this.Sectors + }; + } + + public override string ToString() + { + return String.Format("Cylinder:{0}, Heads:{1}, Sector:{2}", Cylinder, Heads, Sectors); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(DiskGeometry)) return false; + return Equals((DiskGeometry)obj); + } + + private bool Equals(DiskGeometry other) + { + return other.Cylinder == Cylinder && other.Heads == Heads && other.Sectors == Sectors; + } + + public override int GetHashCode() + { + unchecked + { + int result = Cylinder.GetHashCode(); + result = (result * 397) ^ Heads.GetHashCode(); + result = (result * 397) ^ Sectors.GetHashCode(); + return result; + } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/DiskType.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/DiskType.cs new file mode 100644 index 000000000000..26ba48bc5835 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/DiskType.cs @@ -0,0 +1,24 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public enum DiskType + { + None = 0, + Fixed = 2, + Dynamic = 3, + Differencing = 4 + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/HostOsType.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/HostOsType.cs new file mode 100644 index 000000000000..b65b44b61104 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/HostOsType.cs @@ -0,0 +1,22 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public enum HostOsType + { + Windows = 0x5769326B, + Macintosh = 0x4D616320 + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/IndexRange.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/IndexRange.cs new file mode 100644 index 000000000000..c1b6869a8dd4 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/IndexRange.cs @@ -0,0 +1,214 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class IndexRange : IEquatable + { + static IndexRangeComparer comparer = new IndexRangeComparer(); + + public static IEnumerable SubstractRanges(IEnumerable source, IEnumerable ranges) + { + var onlyInSource = source.Where(e => !ranges.Any(r => r.Intersects(e))); + + var irs = + from ur in ranges + from r in source + where r.Intersects(ur) + from ir in r.Subtract(ur) + select ir; + + var result = irs.Distinct(new IndexRangeComparer()).ToList(); + result.AddRange(onlyInSource); + result.Sort((r1, r2) => r1.CompareTo(r2)); + return result; + } + + public static IndexRange FromLength(long startIndex, long length) + { + return new IndexRange(startIndex, startIndex + length - 1); + } + + public IndexRange(long startIndex, long endIndex) + { + this.StartIndex = startIndex; + this.EndIndex = endIndex; + } + + public bool After(IndexRange range) + { + if (Intersects(range)) + { + return false; + } + + return (this.StartIndex > range.EndIndex); + } + + public IEnumerable Subtract(IndexRange range) + { + if (this.Equals(range)) + { + return new List(); + } + if (!this.Intersects(range)) + { + return new List { this }; + } + var intersection = this.Intersection(range); + if (this.Equals(intersection)) + { + return new List(); + } + if (intersection.StartIndex == this.StartIndex) + { + return new List { new IndexRange(intersection.EndIndex + 1, this.EndIndex) }; + } + if (intersection.EndIndex == this.EndIndex) + { + return new List { new IndexRange(this.StartIndex, intersection.StartIndex - 1) }; + } + return new List + { + new IndexRange(this.StartIndex, intersection.StartIndex - 1), + new IndexRange(intersection.EndIndex + 1, this.EndIndex) + }; + } + + public bool Abuts(IndexRange range) + { + return !this.Intersects(range) && this.Gap(range) == null; + } + + public IndexRange Gap(IndexRange range) + { + if (this.Intersects(range)) + return null; + if (this.CompareTo(range) > 0) + { + var r = new IndexRange(range.EndIndex + 1, this.StartIndex - 1); + if (r.Length <= 0) + return null; + return r; + } + var result = new IndexRange(this.EndIndex + 1, range.StartIndex - 1); + if (result.Length <= 0) + return null; + return result; + } + + public int CompareTo(IndexRange range) + { + return this.StartIndex != range.StartIndex ? + this.StartIndex.CompareTo(range.StartIndex) : + this.EndIndex.CompareTo(range.EndIndex); + } + + public IndexRange Merge(IndexRange range) + { + if (!this.Abuts(range)) + { + throw new ArgumentOutOfRangeException("range", "Ranges must be adjacent."); + } + if (this.CompareTo(range) > 0) + { + return new IndexRange(range.StartIndex, this.EndIndex); + } + return new IndexRange(this.StartIndex, range.EndIndex); + } + + public IEnumerable PartitionBy(int size) + { + if (this.Length <= size) + { + return new List { this }; + } + var result = new List(); + long count = this.Length / size; + long remainder = this.Length % size; + for (long i = 0; i < count; i++) + { + result.Add(IndexRange.FromLength(this.StartIndex + i * size, size)); + } + if (remainder != 0) + { + result.Add(IndexRange.FromLength(this.StartIndex + count * size, remainder)); + } + return result; + } + + public IndexRange Intersection(IndexRange range) + { + if (!this.Intersects(range)) + { + return null; + } + var start = Math.Max(range.StartIndex, this.StartIndex); + var end = Math.Min(range.EndIndex, this.EndIndex); + + return new IndexRange(start, end); + } + + public bool Intersects(IndexRange range) + { + var start = Math.Max(range.StartIndex, this.StartIndex); + var end = Math.Min(range.EndIndex, this.EndIndex); + return start <= end; + } + + public bool Includes(IndexRange range) + { + return this.Includes(range.StartIndex) && this.Includes(range.EndIndex); + } + + public long EndIndex { get; private set; } + + public long StartIndex { get; private set; } + + public bool Includes(long value) + { + return value >= StartIndex && value <= EndIndex; + } + + public long Length + { + get { return EndIndex - StartIndex + 1; } + } + + public bool Equals(IndexRange other) + { + return other != null && this.StartIndex == other.StartIndex && this.EndIndex == other.EndIndex; + } + + public override string ToString() + { + return String.Format("[{0},{1}]", StartIndex, EndIndex); + } + + public override bool Equals(object obj) + { + var range = obj as IndexRange; + return range != null && comparer.Equals(this, range); + } + + public override int GetHashCode() + { + return comparer.GetHashCode(this); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/IndexRangeComparer.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/IndexRangeComparer.cs new file mode 100644 index 000000000000..342014e82d87 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/IndexRangeComparer.cs @@ -0,0 +1,33 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class IndexRangeComparer : IEqualityComparer + { + public bool Equals(IndexRange first, IndexRange second) + { + return first.Equals(second); + } + + public int GetHashCode(IndexRange range) + { + var hash = 17 + range.StartIndex.GetHashCode(); + hash = hash * 17 + range.EndIndex.GetHashCode(); + return hash; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Model.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Model.cs new file mode 100644 index 000000000000..1e34e498b2ba --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Model.cs @@ -0,0 +1,162 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Common.General; +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + [Flags] + public enum VhdValidationType + { + None, + IsVhd, + FixedDisk, + } + + public class VhdValidator + { + public static IList Validate(VhdValidationType validation, string path) + { + var fileFactory = new VhdFileFactory(); + VhdFile vhdFile = null; + Exception exception = null; + try + { + vhdFile = fileFactory.Create(path); + } + catch (VhdParsingException e) + { + exception = e; + } + + return DoValidate(validation, vhdFile, exception); + } + + public static IList Validate(VhdValidationType validation, Stream vhdStream) + { + + var fileFactory = new VhdFileFactory(); + VhdFile vhdFile = null; + Exception exception = null; + try + { + vhdFile = fileFactory.Create(vhdStream); + } + catch (VhdParsingException e) + { + exception = e; + } + + return DoValidate(validation, vhdFile, exception); + } + + private static IList DoValidate(VhdValidationType validation, VhdFile vhdFile, Exception exception) + { + var result = new List(); + + if ((validation & VhdValidationType.IsVhd) == VhdValidationType.IsVhd) + { + var validationResult = new VhdValidationResult + { + ValidationType = VhdValidationType.IsVhd + }; + if (vhdFile == null) + { + validationResult.ErrorCode = 1000; + validationResult.Error = exception; + } + result.Add(validationResult); + } + + if ((validation & VhdValidationType.FixedDisk) == VhdValidationType.FixedDisk) + { + var validationResult = new VhdValidationResult + { + ValidationType = VhdValidationType.FixedDisk + }; + if (vhdFile == null || vhdFile.Footer.DiskType != DiskType.Fixed) + { + validationResult.ErrorCode = 1001; + } + result.Add(validationResult); + } + return result; + } + + public static IAsyncResult BeginValidate(VhdValidationType validation, Stream vhdStream, AsyncCallback callback, object state) + { + return AsyncMachine>.BeginAsyncMachine(ValidateAsync, validation, vhdStream, callback, state); + } + + public static IList EndValidate(IAsyncResult result) + { + return AsyncMachine>.EndAsyncMachine(result); + } + + private static IEnumerable ValidateAsync(AsyncMachine> machine, VhdValidationType validation, Stream vhdStream) + { + var result = new List(); + + var fileFactory = new VhdFileFactory(); + + fileFactory.BeginCreate(vhdStream, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + + VhdFile vhdFile = null; + Exception exception = null; + try + { + vhdFile = fileFactory.EndCreate(machine.CompletionResult); + } + catch (VhdParsingException e) + { + exception = e; + } + + machine.ParameterValue = DoValidate(validation, vhdFile, exception); + } + } + + public class VhdValidationResult + { + public int ErrorCode { get; set; } + public VhdValidationType ValidationType { get; set; } + public Exception Error { get; set; } + } + + [Serializable] + public class VhdParsingException : Exception + { + public VhdParsingException() + { + } + + public VhdParsingException(string message) : base(message) + { + } + + public VhdParsingException(string message, Exception innerException) : base(message, innerException) + { + } + + protected VhdParsingException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/ParentLocator.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/ParentLocator.cs new file mode 100644 index 000000000000..ed26eb462628 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/ParentLocator.cs @@ -0,0 +1,39 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + + + [VhdEntity(Size = 24)] + public class ParentLocator + { + [VhdProperty(Offset = 0, Size = 4)] + public PlatformCode PlatformCode { get; set; } + + [VhdProperty(Offset = 4, Size = 4)] + public int PlatformDataSpace { get; set; } + + [VhdProperty(Offset = 8, Size = 4)] + public int PlatformDataLength { get; set; } + + [VhdProperty(Offset = 12, Size = 4)] + public int Reserved { get; set; } + + [VhdProperty(Offset = 16, Size = 8)] + public long PlatformDataOffset { get; set; } + + public string PlatformSpecificFileLocator { get; set; } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/AbstractDiskBlockFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/AbstractDiskBlockFactory.cs new file mode 100644 index 000000000000..0d15d38f45e7 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/AbstractDiskBlockFactory.cs @@ -0,0 +1,73 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public abstract class AbstractDiskBlockFactory : IBlockFactory + { + protected readonly VhdFile vhdFile; + private byte[] emptyBlockData; + + protected AbstractDiskBlockFactory(VhdFile vhdFile) + { + this.vhdFile = vhdFile; + } + + public abstract Block Create(uint block); + public abstract Sector GetSector(Block block, uint sector); + + public IndexRange GetFooterRange() + { + return IndexRange.FromLength(this.GetBlockSize() * this.BlockCount, VhdConstants.VHD_FOOTER_SIZE); + } + + public byte[] ReadBlockData(Block block) + { + if (!this.HasData(block.BlockIndex)) + { + if (emptyBlockData == null) + { + emptyBlockData = new byte[(int)GetBlockSize()]; + Array.Clear(emptyBlockData, 0, emptyBlockData.Length); + } + return emptyBlockData; + } + return DoReadBlockData(block); + } + + protected abstract byte[] DoReadBlockData(Block block); + + public long BlockCount + { + get { return vhdFile.BlockAllocationTable.Size; } + } + + public bool HasData(uint blockIndex) + { + return vhdFile.BlockAllocationTable.HasData(blockIndex); + } + + public long GetBlockAddress(uint blockIndex) + { + return vhdFile.BlockAllocationTable.GetBlockAddress(blockIndex); + } + + public long GetBlockSize() + { + return vhdFile.Header.BlockSize; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/BitMapFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/BitMapFactory.cs new file mode 100644 index 000000000000..d25ff13a0d44 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/BitMapFactory.cs @@ -0,0 +1,50 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Collections; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class BitMapFactory + { + private readonly VhdFile vhdFile; + + public BitMapFactory(VhdFile vhdFile) + { + this.vhdFile = vhdFile; + } + + public BitMap Create(uint block) + { + var bitMapAddress = vhdFile.BlockAllocationTable.GetBitMapAddress(block); + var bitmapSizeInBytes = vhdFile.BlockAllocationTable.GetBitmapSizeInBytes(); + var bytes = vhdFile.DataReader.ReadBytes(bitMapAddress, bitmapSizeInBytes); + ReverseBitsIfLittleEndian(bytes); + return new BitMap(new BitArray(bytes)); + } + + private static void ReverseBitsIfLittleEndian(byte[] bytes) + { + if (BitConverter.IsLittleEndian) + { + for (int bit = 0; bit < bytes.Length; bit++) + { + // reverse the bits due to quirky BitArray + bytes[bit] = (byte)(((bytes[bit] * 0x80200802UL) & 0x0884422110UL) * 0x0101010101UL >> 32); + } + } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/BlockAllocationTableFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/BlockAllocationTableFactory.cs new file mode 100644 index 000000000000..80d024f80e17 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/BlockAllocationTableFactory.cs @@ -0,0 +1,68 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Common.General; +using System; +using System.Collections.Generic; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class BlockAllocationTableFactory + { + private readonly VhdDataReader dataReader; + private readonly VhdHeader header; + + public BlockAllocationTableFactory(VhdDataReader dataReader, VhdHeader header) + { + this.dataReader = dataReader; + this.header = header; + } + + public BlockAllocationTable Create() + { + dataReader.SetPosition(header.TableOffset); + + var bat = new uint[header.MaxTableEntries]; + for (int block = 0; block < header.MaxTableEntries; block++) + { + bat[block] = dataReader.ReadUInt32(); + } + return new BlockAllocationTable(header.MaxTableEntries, header.BlockSize, bat); + } + + public IAsyncResult BeginCreate(AsyncCallback callback, object state) + { + return AsyncMachine.BeginAsyncMachine(CreateAsync, callback, state); + } + + public BlockAllocationTable EndCreate(IAsyncResult result) + { + return AsyncMachine.EndAsyncMachine(result); + } + + private IEnumerable CreateAsync(AsyncMachine machine) + { + dataReader.SetPosition(header.TableOffset); + + var bat = new uint[header.MaxTableEntries]; + for (int block = 0; block < header.MaxTableEntries; block++) + { + dataReader.BeginReadUInt32(machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + bat[block] = dataReader.EndReadUInt32(machine.CompletionResult); + } + machine.ParameterValue = new BlockAllocationTable(header.MaxTableEntries, header.BlockSize, bat); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/CloudVhdFileCreator.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/CloudVhdFileCreator.cs new file mode 100644 index 000000000000..4168b49cc802 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/CloudVhdFileCreator.cs @@ -0,0 +1,60 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Common.General; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class VhdFileCreator + { + public static void CreateFixedVhdFile(Stream destination, long virtualSize) + { + var footer = VhdFooterFactory.CreateFixedDiskFooter(virtualSize); + var serializer = new VhdFooterSerializer(footer); + var buffer = serializer.ToByteArray(); + destination.SetLength(virtualSize + VhdConstants.VHD_FOOTER_SIZE); + destination.Seek(-VhdConstants.VHD_FOOTER_SIZE, SeekOrigin.End); + + destination.Write(buffer, 0, buffer.Length); + destination.Flush(); + } + + public static IAsyncResult BeginCreateFixedVhdFile(Stream destination, long size, AsyncCallback callback, object state) + { + return AsyncMachine.BeginAsyncMachine(CreateFixedVhdFileAtAsync, destination, size, callback, state); + } + + public static void EndCreateFixedVhdFile(IAsyncResult result) + { + AsyncMachine.EndAsyncMachine(result); + } + + private static IEnumerable CreateFixedVhdFileAtAsync(AsyncMachine machine, Stream destination, long virtualSize) + { + var footer = VhdFooterFactory.CreateFixedDiskFooter(virtualSize); + var serializer = new VhdFooterSerializer(footer); + var buffer = serializer.ToByteArray(); + destination.SetLength(virtualSize + VhdConstants.VHD_FOOTER_SIZE); + destination.Seek(-VhdConstants.VHD_FOOTER_SIZE, SeekOrigin.End); + + destination.BeginWrite(buffer, 0, buffer.Length, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + destination.EndWrite(machine.CompletionResult); + destination.Flush(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DifferencingDiskBlockFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DifferencingDiskBlockFactory.cs new file mode 100644 index 000000000000..a30b63bd918b --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DifferencingDiskBlockFactory.cs @@ -0,0 +1,87 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class DifferencingDiskBlockFactory : AbstractDiskBlockFactory + { + private BitMapFactory bitMapFactory; + private SectorFactory sectorFactory; + private IBlockFactory parentBlockFactory; + private Block cachedBlock; + + public DifferencingDiskBlockFactory(VhdFile vhdFile) : base(vhdFile) + { + this.bitMapFactory = new BitMapFactory(vhdFile); + this.sectorFactory = new SectorFactory(vhdFile, this); + this.parentBlockFactory = vhdFile.Parent.DiskType != DiskType.Fixed ? vhdFile.Parent.GetBlockFactory() : new FixedDiskBlockFactory(vhdFile.Parent, this.GetBlockSize()); + } + + public override Block Create(uint block) + { + if (!vhdFile.BlockAllocationTable.HasData(block)) + { + if (cachedBlock == null || cachedBlock.BlockIndex != block) + { + cachedBlock = parentBlockFactory.Create(block); + } + return cachedBlock; + } + + if (cachedBlock == null || cachedBlock.BlockIndex != block) + { + cachedBlock = new Block(this) + { + BlockIndex = block, + VhdUniqueId = this.vhdFile.Footer.UniqueId, + LogicalRange = IndexRange.FromLength(block * GetBlockSize(), vhdFile.Header.BlockSize), + BitMap = bitMapFactory.Create(block), + Empty = false + }; + } + return cachedBlock; + } + + public override Sector GetSector(Block block, uint sector) + { + if (block.Empty) + { + return this.sectorFactory.CreateEmptySector(block.BlockIndex, sector); + } + + if (block.BitMap != null && block.BitMap.Data[(int)sector]) + { + return this.sectorFactory.Create(block, sector); + } + + var parentBlock = parentBlockFactory.Create(block.BlockIndex); + return parentBlockFactory.GetSector(parentBlock, sector); + } + + protected override byte[] DoReadBlockData(Block block) + { + var result = new byte[GetBlockSize()]; + int index = 0; + for (int i = 0; i < block.SectorCount; i++) + { + var sector = block.GetSector((uint)i); + Buffer.BlockCopy(sector.Data, 0, result, index, sector.Data.Length); + index += sector.Data.Length; + } + return result; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DiskTypeFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DiskTypeFactory.cs new file mode 100644 index 000000000000..55b52edc1f12 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DiskTypeFactory.cs @@ -0,0 +1,34 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class DiskTypeFactory + { + public DiskType Create(uint diskType) + { + switch (diskType) + { + case 0: return DiskType.None; + case 2: return DiskType.Fixed; + case 3: return DiskType.Dynamic; + case 4: return DiskType.Differencing; + default: + throw new VhdParsingException(String.Format("Unsupported format: DiskType is {0}", diskType)); + } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DynamicDiskBlockFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DynamicDiskBlockFactory.cs new file mode 100644 index 000000000000..193c19fcdda5 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/DynamicDiskBlockFactory.cs @@ -0,0 +1,77 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + + + public class DynamicDiskBlockFactory : AbstractDiskBlockFactory + { + private BitMapFactory bitMapFactory; + private SectorFactory sectorFactory; + private Block cachedBlock; + + public DynamicDiskBlockFactory(VhdFile vhdFile) : base(vhdFile) + { + this.bitMapFactory = new BitMapFactory(vhdFile); + this.sectorFactory = new SectorFactory(vhdFile, this); + } + + public override Block Create(uint block) + { + if (!vhdFile.BlockAllocationTable.HasData(block)) + { + if (cachedBlock == null || cachedBlock.BlockIndex != block) + { + cachedBlock = new Block(this) + { + BlockIndex = block, + VhdUniqueId = this.vhdFile.Footer.UniqueId, + LogicalRange = IndexRange.FromLength(block * GetBlockSize(), vhdFile.Header.BlockSize), + BitMap = null, + Empty = true + }; + } + return cachedBlock; + } + + if (cachedBlock == null || cachedBlock.BlockIndex != block) + { + cachedBlock = new Block(this) + { + BlockIndex = block, + VhdUniqueId = this.vhdFile.Footer.UniqueId, + LogicalRange = IndexRange.FromLength(block * GetBlockSize(), vhdFile.Header.BlockSize), + BitMap = bitMapFactory.Create(block), + Empty = false + }; + } + return cachedBlock; + } + + public override Sector GetSector(Block block, uint sector) + { + if (block.Empty) + { + return this.sectorFactory.CreateEmptySector(block.BlockIndex, sector); + } + return this.sectorFactory.Create(block, sector); + } + + protected override byte[] DoReadBlockData(Block block) + { + return vhdFile.DataReader.ReadBytes(GetBlockAddress(block.BlockIndex), (int)GetBlockSize()); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/FixedDiskBlockFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/FixedDiskBlockFactory.cs new file mode 100644 index 000000000000..8f0232cffa1c --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/FixedDiskBlockFactory.cs @@ -0,0 +1,132 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class FixedDiskBlockFactory : IBlockFactory + { + private readonly VhdFile vhdFile; + private SectorFactory sectorFactory; + private Block cachedBlock; + private long blockSize; + private long? extraBlockIndex; + + public FixedDiskBlockFactory(VhdFile vhdFile) : this(vhdFile, VhdConstants.VHD_DEFAULT_BLOCK_SIZE) + { + } + + public FixedDiskBlockFactory(VhdFile vhdFile, long blockSize) + { + this.vhdFile = vhdFile; + this.blockSize = blockSize; + this.BlockCount = CalculateBlockCount(); + this.sectorFactory = new SectorFactory(vhdFile, this); + } + + private int CalculateBlockCount() + { + var count = this.vhdFile.Footer.VirtualSize * 1.0m / this.GetBlockSize(); + if (Math.Floor(count) < Math.Ceiling(count)) + { + extraBlockIndex = (long)Math.Floor(count); + } + return (int)Math.Ceiling(count); + } + + public long BlockCount { get; private set; } + + public Block Create(uint block) + { + if (!this.HasData(block)) + { + if (cachedBlock == null || cachedBlock.BlockIndex != block) + { + IndexRange logicalRange = IndexRange.FromLength(block * GetBlockSize(), this.GetBlockSize()); + if (extraBlockIndex.HasValue && block == extraBlockIndex) + { + long startIndex = block * GetBlockSize(); + long size = this.vhdFile.DataReader.Size - startIndex - VhdConstants.VHD_FOOTER_SIZE; + logicalRange = IndexRange.FromLength(startIndex, size); + } + cachedBlock = new Block(this) + { + BlockIndex = block, + VhdUniqueId = this.vhdFile.Footer.UniqueId, + LogicalRange = logicalRange, + BitMap = null, + Empty = true + }; + } + return cachedBlock; + } + if (cachedBlock == null || cachedBlock.BlockIndex != block) + { + IndexRange logicalRange = IndexRange.FromLength(block * GetBlockSize(), this.GetBlockSize()); + if (extraBlockIndex.HasValue && block == extraBlockIndex) + { + long startIndex = block * GetBlockSize(); + long size = this.vhdFile.DataReader.Size - startIndex - VhdConstants.VHD_FOOTER_SIZE; + logicalRange = IndexRange.FromLength(startIndex, size); + } + cachedBlock = new Block(this) + { + BlockIndex = block, + VhdUniqueId = this.vhdFile.Footer.UniqueId, + LogicalRange = logicalRange, + Empty = false + }; + } + return cachedBlock; + } + + public byte[] ReadBlockData(Block block) + { + long blockAddress = GetBlockAddress(block.BlockIndex); + return vhdFile.DataReader.ReadBytes(blockAddress, (int)block.LogicalRange.Length); + } + + public Sector GetSector(Block block, uint sector) + { + if (block.Empty) + { + return this.sectorFactory.CreateEmptySector(block.BlockIndex, sector); + } + return this.sectorFactory.Create(block, sector); + } + + public IndexRange GetFooterRange() + { + long startIndex = this.vhdFile.DataReader.Size - VhdConstants.VHD_FOOTER_SIZE; + var logicalRange = IndexRange.FromLength(startIndex, VhdConstants.VHD_FOOTER_SIZE); + return logicalRange; + } + + public bool HasData(uint blockIndex) + { + return blockIndex != VhdConstants.VHD_NO_DATA_INT; + } + + public long GetBlockAddress(uint blockIndex) + { + return blockIndex * this.blockSize; + } + + public long GetBlockSize() + { + return this.blockSize; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/IBlockFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/IBlockFactory.cs new file mode 100644 index 000000000000..e780a6daf794 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/IBlockFactory.cs @@ -0,0 +1,32 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public interface IBlockFactory + { + long BlockCount { get; } + + long GetBlockSize(); + long GetBlockAddress(uint blockIndex); + bool HasData(uint blockIndex); + + Block Create(uint block); + byte[] ReadBlockData(Block block); + + Sector GetSector(Block block, uint sector); + + IndexRange GetFooterRange(); + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/SectorFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/SectorFactory.cs new file mode 100644 index 000000000000..f800a9b4b5fa --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/SectorFactory.cs @@ -0,0 +1,71 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class SectorFactory + { + private readonly VhdFile vhdFile; + private readonly IBlockFactory blockFactory; + + public SectorFactory(VhdFile vhdFile, IBlockFactory blockFactory) + { + this.vhdFile = vhdFile; + this.blockFactory = blockFactory; + } + + public Sector Create(Block blockArg, uint sector) + { + uint block = blockArg.BlockIndex; + long totalSectors = blockArg.SectorCount; + if (sector > totalSectors) + { + string message = String.Format("TotalSectors: {0}, Requested Sector:{1}", totalSectors, sector); + throw new ArgumentOutOfRangeException("sector", message); + } + if (!blockFactory.HasData(block)) + { + return CreateEmptySector(block, sector); + } + + long currentAddress = blockFactory.GetBlockAddress(block); + vhdFile.DataReader.SetPosition(currentAddress + (int)VhdConstants.VHD_SECTOR_LENGTH * sector); + + var result = new Sector + { + BlockIndex = block, + SectorIndex = sector, + GlobalSectorIndex = this.blockFactory.GetBlockSize() * block + sector, + Data = vhdFile.DataReader.ReadBytes((int)VhdConstants.VHD_SECTOR_LENGTH) + }; + return result; + } + + public Sector CreateEmptySector(uint block, uint sector) + { + var buffer = new byte[((int)VhdConstants.VHD_SECTOR_LENGTH)]; + Array.Clear(buffer, 0, buffer.Length); + var emptySector = new Sector + { + BlockIndex = block, + SectorIndex = sector, + GlobalSectorIndex = this.blockFactory.GetBlockSize() * block + sector, + Data = buffer + }; + return emptySector; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/StreamHelper.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/StreamHelper.cs new file mode 100644 index 000000000000..ff09ddbbcf6a --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/StreamHelper.cs @@ -0,0 +1,95 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Common.General; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class StreamHelper + { + public static IAsyncResult BeginReadBytes(Stream stream, long offset, int length, AsyncCallback callback, object state) + { + stream.Seek(offset, SeekOrigin.Begin); + return AsyncMachine.BeginAsyncMachine(ReadBytesAsync, stream, length, callback, state); + } + + public static IAsyncResult BeginReadBytes(Stream stream, long offset, int length, SeekOrigin origin, AsyncCallback callback, object state) + { + stream.Seek(-offset, SeekOrigin.End); + return AsyncMachine.BeginAsyncMachine(ReadBytesAsync, stream, length, callback, state); + } + + public static byte[] EndReadBytes(IAsyncResult result) + { + return AsyncMachine.EndAsyncMachine(result); + } + + public static IEnumerable ReadBytesAsync(AsyncMachine machine, Stream stream, int length) + { + var attributeHelper = new AttributeHelper(); + var buffer = new byte[length]; + + int readCount = 0; + int remaining = length; + while (remaining > 0) + { + stream.BeginRead(buffer, readCount, remaining, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + var currentRead = stream.EndRead(machine.CompletionResult); + if (currentRead == 0) + { + break; + } + readCount += currentRead; + remaining -= currentRead; + } + machine.ParameterValue = buffer; + yield break; + } + + public static byte[] ReadBytes(Stream stream, long offset, int length) + { + stream.Seek(offset, SeekOrigin.Begin); + return ReadBytes(stream, length); + } + + public static byte[] ReadBytes(Stream stream, long offset, int length, SeekOrigin origin) + { + stream.Seek(-offset, origin); + return ReadBytes(stream, length); + } + + private static byte[] ReadBytes(Stream stream, int length) + { + var buffer = new byte[length]; + int readCount = 0; + int remaining = length; + while (remaining > 0) + { + var currentRead = stream.Read(buffer, readCount, remaining); + if (currentRead == 0) + { + break; + } + readCount += currentRead; + remaining -= currentRead; + } + + return buffer; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdConstants.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdConstants.cs new file mode 100644 index 000000000000..a2f6c18eddcc --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdConstants.cs @@ -0,0 +1,28 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class VhdConstants + { + public const long VHD_DEFAULT_BLOCK_SIZE = 512 * 1024; + public const long VHD_NO_DATA_LONG = ~0L; + public const uint VHD_NO_DATA_INT = 0xFFFFFFFF; + + public const long VHD_PAGE_SIZE = 512; + public const long VHD_FOOTER_SIZE = 512; + public const long VHD_SECTOR_LENGTH = 512; + public const long VHD_FOOTER_OFFSET_CHECKSUM = 64; + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdDataReader.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdDataReader.cs new file mode 100644 index 000000000000..421c8cf612be --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdDataReader.cs @@ -0,0 +1,292 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Common.General; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class VhdDataReader + { + private readonly BinaryReader reader; + private byte[] m_buffer; + + public VhdDataReader(BinaryReader reader) + { + this.reader = reader; + this.m_buffer = new byte[16]; + } + + public long Size + { + get { return this.reader.BaseStream.Length; } + } + + public bool ReadBoolean(long offset) + { + this.SetPosition(offset); + return this.reader.ReadBoolean(); + } + + public IAsyncResult BeginReadBoolean(long offset, AsyncCallback callback, object state) + { + this.SetPosition(offset); + return AsyncMachine.BeginAsyncMachine(FillBuffer, 1, callback, state); + } + + public bool EndReadBoolean(IAsyncResult result) + { + AsyncMachine.EndAsyncMachine(result); + return (m_buffer[0] != 0); + } + + IEnumerable FillBuffer(AsyncMachine machine, int numBytes) + { + if (m_buffer != null && (numBytes < 0 || numBytes > m_buffer.Length)) + { + throw new ArgumentOutOfRangeException("numBytes", String.Format("Expected (0-16) however found: {0}", numBytes)); + } + int bytesRead = 0; + int n = 0; + + // Need to find a good threshold for calling ReadByte() repeatedly + // vs. calling Read(byte[], int, int) for both buffered & unbuffered + // streams. + if (numBytes == 1) + { + this.reader.BaseStream.BeginRead(m_buffer, 0, numBytes, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + n = this.reader.BaseStream.EndRead(machine.CompletionResult); + if (n == -1) + { + throw new EndOfStreamException(); + } + m_buffer[0] = (byte)n; + } + + do + { + this.reader.BaseStream.BeginRead(m_buffer, bytesRead, numBytes - bytesRead, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + n = this.reader.BaseStream.EndRead(machine.CompletionResult); + + if (n == 0) + { + throw new EndOfStreamException(); + } + bytesRead += n; + } while (bytesRead < numBytes); + } + + public short ReadInt16(long offset) + { + this.SetPosition(offset); + return IPAddress.NetworkToHostOrder((short)this.reader.ReadUInt16()); + } + + public IAsyncResult BeginReadInt16(long offset, AsyncCallback callback, object state) + { + this.SetPosition(offset); + return AsyncMachine.BeginAsyncMachine(FillBuffer, 2, callback, state); + } + + public short EndReadInt16(IAsyncResult result) + { + AsyncMachine.EndAsyncMachine(result); + short value = (short)(m_buffer[0] | m_buffer[1] << 8); + return IPAddress.NetworkToHostOrder(value); + } + + public uint ReadUInt32(long offset) + { + this.SetPosition(offset); + return (uint)IPAddress.NetworkToHostOrder((int)this.reader.ReadUInt32()); + } + + public IAsyncResult BeginReadUInt32(long offset, AsyncCallback callback, object state) + { + this.SetPosition(offset); + return AsyncMachine.BeginAsyncMachine(FillBuffer, 4, callback, state); + } + + public uint EndReadUInt32(IAsyncResult result) + { + AsyncMachine.EndAsyncMachine(result); + var value = (m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24); + return (uint)IPAddress.NetworkToHostOrder(value); + } + + public uint ReadUInt32() + { + return (uint)IPAddress.NetworkToHostOrder((int)this.reader.ReadUInt32()); + } + + public IAsyncResult BeginReadUInt32(AsyncCallback callback, object state) + { + return AsyncMachine.BeginAsyncMachine(FillBuffer, 4, callback, state); + } + + public ulong ReadUInt64(long offset) + { + this.SetPosition(offset); + var value = (long)this.reader.ReadUInt64(); + return (ulong)IPAddress.NetworkToHostOrder(value); + } + + public IAsyncResult BeginReadUInt64(long offset, AsyncCallback callback, object state) + { + this.SetPosition(offset); + return AsyncMachine.BeginAsyncMachine(FillBuffer, 8, callback, state); + } + + public ulong EndReadUInt64(IAsyncResult result) + { + AsyncMachine.EndAsyncMachine(result); + uint lo = (uint)(m_buffer[0] | m_buffer[1] << 8 | + m_buffer[2] << 16 | m_buffer[3] << 24); + uint hi = (uint)(m_buffer[4] | m_buffer[5] << 8 | + m_buffer[6] << 16 | m_buffer[7] << 24); + ulong value = ((ulong)hi) << 32 | lo; + return (ulong)IPAddress.NetworkToHostOrder((long)value); + } + + public byte[] ReadBytes(long offset, int count) + { + this.SetPosition(offset); + return this.reader.ReadBytes(count); + } + + public IAsyncResult BeginReadBytes(long offset, int count, AsyncCallback callback, object state) + { + this.SetPosition(offset); + return AsyncMachine.BeginAsyncMachine(ReadBytesAsync, offset, count, callback, state); + } + + public byte[] EndReadBytes(IAsyncResult result) + { + return AsyncMachine.EndAsyncMachine(result); + } + + private IEnumerable ReadBytesAsync(AsyncMachine machine, long offset, int count) + { + StreamHelper.BeginReadBytes(this.reader.BaseStream, offset, count, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + byte[] values = StreamHelper.EndReadBytes(machine.CompletionResult); + machine.ParameterValue = values; + } + + public byte[] ReadBytes(int count) + { + return this.reader.ReadBytes(count); + } + + public IAsyncResult BeginReadBytes(int count, AsyncCallback callback, object state) + { + return AsyncMachine.BeginAsyncMachine(ReadBytesAsync, this.reader.BaseStream.Position, count, callback, state); + } + + public string ReadString(int count) + { + return Encoding.ASCII.GetString(this.reader.ReadBytes(count)); + } + + public IAsyncResult BeginReadString(int count, AsyncCallback callback, object state) + { + return AsyncMachine.BeginAsyncMachine(ReadStringAsync, this.reader.BaseStream.Position, count, callback, state); + } + + public string EndReadString(IAsyncResult result) + { + return AsyncMachine.EndAsyncMachine(result); + } + + private IEnumerable ReadStringAsync(AsyncMachine machine, long offset, int count) + { + BeginReadBytes(offset, count, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + byte[] values = EndReadBytes(machine.CompletionResult); + machine.ParameterValue = Encoding.ASCII.GetString(values); + } + + public byte ReadByte(long offset) + { + this.SetPosition(offset); + return this.reader.ReadByte(); + } + + public IAsyncResult BeginReadByte(long offset, AsyncCallback callback, object state) + { + return BeginReadBytes(offset, 1, callback, state); + } + + public byte EndReadByte(IAsyncResult result) + { + return EndReadBytes(result)[0]; + } + + public Guid ReadGuid(long offset) + { + return new Guid(this.ReadBytes(offset, 16)); + } + + public IAsyncResult BeginReadGuid(long offset, AsyncCallback callback, object state) + { + return BeginReadBytes(offset, 16, callback, state); + } + + public Guid EndReadGuid(IAsyncResult result) + { + byte[] guidValue = EndReadBytes(result); + return new Guid(guidValue); + } + + public DateTime ReadDateTime(long offset) + { + var timeStamp = new VhdTimeStamp(this.ReadUInt32(offset)); + return timeStamp.ToDateTime(); + } + + public IAsyncResult BeginReadDateTime(long offset, AsyncCallback callback, object state) + { + return BeginReadUInt32(offset, callback, state); + } + + public DateTime EndReadDateTime(IAsyncResult result) + { + uint value = EndReadUInt32(result); + var timeStamp = new VhdTimeStamp(value); + return timeStamp.ToDateTime(); + } + + public DateTime ReadDateTime() + { + var timeStamp = new VhdTimeStamp(this.ReadUInt32()); + return timeStamp.ToDateTime(); + } + + public IAsyncResult BeginReadDateTime(AsyncCallback callback, object state) + { + return BeginReadUInt32(callback, state); + } + + public void SetPosition(long batOffset) + { + this.reader.BaseStream.Seek(batOffset, SeekOrigin.Begin); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdDataWriter.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdDataWriter.cs new file mode 100644 index 000000000000..97f1b87da086 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdDataWriter.cs @@ -0,0 +1,112 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.IO; +using System.Net; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class VhdDataWriter + { + private readonly BinaryWriter writer; + + public VhdDataWriter(BinaryWriter writer) + { + this.writer = writer; + } + + public long Size + { + get { return this.writer.BaseStream.Length; } + } + + public void WriteBoolean(long offset, bool value) + { + this.SetPosition(offset); + this.writer.Write(value); + } + + public void WriteInt(long offset, int value) + { + this.SetPosition(offset); + this.writer.Write((uint)IPAddress.HostToNetworkOrder(value)); + } + + public void WriteInt16(long offset, Int16 value) + { + this.SetPosition(offset); + this.writer.Write(IPAddress.HostToNetworkOrder((short)value)); + } + + public void WriteInt16(Int16 value) + { + this.writer.Write(IPAddress.HostToNetworkOrder((short)value)); + } + + public void WriteUInt(long offset, uint value) + { + this.SetPosition(offset); + this.writer.Write((uint)IPAddress.HostToNetworkOrder((int)value)); + } + + public void WriteUInt(uint value) + { + this.writer.Write((uint)IPAddress.HostToNetworkOrder((int)value)); + } + + public void WriteLong(long offset, long value) + { + this.SetPosition(offset); + var result = (ulong)IPAddress.HostToNetworkOrder(value); + this.writer.Write(result); + } + + public void WriteBytes(long offset, byte[] value) + { + this.SetPosition(offset); + this.writer.Write(value); + } + + public void WriteByte(long offset, byte value) + { + this.SetPosition(offset); + this.writer.Write(value); + } + + public void WriteByte(byte value) + { + this.writer.Write(value); + } + + public void WriteGuid(long offset, Guid value) + { + this.SetPosition(offset); + this.writer.Write(value.ToByteArray()); + } + + public void WriteTimeStamp(long offset, DateTime value) + { + this.SetPosition(offset); + var timeStamp = new VhdTimeStamp(value); + uint result = (uint)IPAddress.HostToNetworkOrder((int)timeStamp.TotalSeconds); + this.writer.Write(result); + } + + public void SetPosition(long batOffset) + { + this.writer.BaseStream.Seek(batOffset, SeekOrigin.Begin); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFileFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFileFactory.cs new file mode 100644 index 000000000000..4ba91ae81a93 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFileFactory.cs @@ -0,0 +1,212 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Common.General; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class VhdFileFactory + { + public VhdFile Create(string path) + { + var streamSource = new StreamSource + { + Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024), + VhdDirectory = Path.GetDirectoryName(path), + DisposeOnException = true + }; + return Create(streamSource); + } + + public VhdFile Create(Stream stream) + { + return Create(new StreamSource { Stream = stream }); + } + + private VhdFile Create(StreamSource streamSource) + { + var disposer = new Action(() => { if (streamSource.DisposeOnException) streamSource.Stream.Dispose(); }); + bool throwing = false; + try + { + var reader = new BinaryReader(streamSource.Stream, Encoding.Unicode); + var dataReader = new VhdDataReader(reader); + var footer = new VhdFooterFactory(dataReader).CreateFooter(); + + VhdHeader header = null; + BlockAllocationTable blockAllocationTable = null; + VhdFile parent = null; + if (footer.DiskType != DiskType.Fixed) + { + header = new VhdHeaderFactory(dataReader, footer).CreateHeader(); + blockAllocationTable = new BlockAllocationTableFactory(dataReader, header).Create(); + if (footer.DiskType == DiskType.Differencing) + { + var parentPath = streamSource.VhdDirectory == null ? header.ParentPath : Path.Combine(streamSource.VhdDirectory, header.GetRelativeParentPath()); + parent = Create(parentPath); + } + } + return new VhdFile(footer, header, blockAllocationTable, parent, streamSource.Stream); + } + catch (Exception e) + { + throwing = true; + throw new VhdParsingException("unsupported format", e); + } + finally + { + if (throwing) + { + disposer(); + } + } + } + + private T TryCatch(Func method, IAsyncResult result) + { + try + { + return method(result); + } + catch (EndOfStreamException e) + { + throw new VhdParsingException("unsupported format", e); + } + } + + private T TryCatch(Func method, Action disposer, IAsyncResult result) + { + bool throwing = true; + T methodResult = default(T); + try + { + methodResult = method(result); + throwing = false; + } + catch (EndOfStreamException e) + { + throw new VhdParsingException("unsupported format", e); + } + finally + { + if (throwing) + { + disposer(); + } + } + return methodResult; + } + + private T TryCatch(Func method, Action disposer) + { + bool throwing = true; + T methodResult = default(T); + try + { + methodResult = method(); + throwing = false; + } + catch (EndOfStreamException e) + { + throw new VhdParsingException("unsupported format", e); + } + finally + { + if (throwing) + { + disposer(); + } + } + return methodResult; + } + + public IAsyncResult BeginCreate(string path, AsyncCallback callback, object state) + { + var streamSource = new StreamSource + { + Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 1024), + VhdDirectory = Path.GetDirectoryName(path), + DisposeOnException = true + }; + return AsyncMachine.BeginAsyncMachine(this.CreateAsync, streamSource, callback, state); + } + + public IAsyncResult BeginCreate(Stream stream, AsyncCallback callback, object state) + { + var streamSource = new StreamSource { Stream = stream }; + return AsyncMachine.BeginAsyncMachine(this.CreateAsync, streamSource, callback, state); + } + + public VhdFile EndCreate(IAsyncResult result) + { + return AsyncMachine.EndAsyncMachine(result); + } + + class StreamSource + { + public Stream Stream { get; set; } + public string VhdDirectory { get; set; } + public bool DisposeOnException { get; set; } + + public StreamSource() + { + this.DisposeOnException = false; + } + } + + private IEnumerable CreateAsync(AsyncMachine machine, StreamSource streamSource) + { + var disposer = new Action(() => { if (streamSource.DisposeOnException) streamSource.Stream.Dispose(); }); + + var reader = TryCatch(() => new BinaryReader(streamSource.Stream, Encoding.Unicode), disposer); + var dataReader = TryCatch(() => new VhdDataReader(reader), disposer); + var footerFactory = TryCatch(() => new VhdFooterFactory(dataReader), disposer); + + footerFactory.BeginCreateFooter(machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + var footer = TryCatch(footerFactory.EndCreateFooter, disposer, machine.CompletionResult); + + VhdHeader header = null; + BlockAllocationTable blockAllocationTable = null; + VhdFile parent = null; + if (footer.DiskType != DiskType.Fixed) + { + var headerFactory = new VhdHeaderFactory(dataReader, footer); + + headerFactory.BeginCreateHeader(machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header = TryCatch(headerFactory.EndCreateHeader, disposer, machine.CompletionResult); + + var tableFactory = new BlockAllocationTableFactory(dataReader, header); + tableFactory.BeginCreate(machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + blockAllocationTable = TryCatch(tableFactory.EndCreate, disposer, machine.CompletionResult); + + if (footer.DiskType == DiskType.Differencing) + { + var parentPath = streamSource.VhdDirectory == null ? header.ParentPath : Path.Combine(streamSource.VhdDirectory, header.GetRelativeParentPath()); + + BeginCreate(parentPath, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + parent = TryCatch(EndCreate, disposer, machine.CompletionResult); + } + } + machine.ParameterValue = new VhdFile(footer, header, blockAllocationTable, parent, streamSource.Stream); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFooterFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFooterFactory.cs new file mode 100644 index 000000000000..57bbfee4aba5 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFooterFactory.cs @@ -0,0 +1,532 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Common.General; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class VhdFooterFactory + { + public const string WindowsAzureCreatorApplicationName = "wa"; + + public static VhdFooter CreateFixedDiskFooter(long virtualSize) + { + var helper = new AttributeHelper(); + var footer = new VhdFooter(); + var reservedAttribute = helper.GetAttribute(() => footer.Reserved); + + footer.Cookie = VhdCookie.CreateFooterCookie(); + footer.Features = VhdFeature.Reserved; + footer.FileFormatVersion = VhdFileFormatVersion.DefaultFileFormatVersion; + footer.HeaderOffset = VhdConstants.VHD_NO_DATA_LONG; + footer.TimeStamp = DateTime.UtcNow; + footer.CreatorApplication = WindowsAzureCreatorApplicationName; + footer.CreatorVersion = VhdCreatorVersion.CSUP2011; + footer.CreatorHostOsType = HostOsType.Windows; + footer.PhsyicalSize = virtualSize; + footer.VirtualSize = virtualSize; + footer.DiskGeometry = DiskGeometry.CreateFromVirtualSize(virtualSize); + footer.DiskType = DiskType.Fixed; + footer.UniqueId = Guid.NewGuid(); + footer.SavedState = false; + footer.Reserved = new byte[reservedAttribute.Size]; + + var footerSerializer = new VhdFooterSerializer(footer); + var byteArray = footerSerializer.ToByteArray(); + + using (var memoryStream = new MemoryStream(byteArray)) + { + var binaryReader = new BinaryReader(memoryStream); + var vhdDataReader = new VhdDataReader(binaryReader); + var footerFactory = new VhdFooterFactory(vhdDataReader); + var vhdFooter = footerFactory.CreateFooter(); + return vhdFooter; + } + } + + private readonly VhdDataReader dataReader; + private readonly DiskTypeFactory diskTypeFactory; + + public VhdFooterFactory(VhdDataReader dataReader) + { + this.dataReader = dataReader; + this.diskTypeFactory = new DiskTypeFactory(); + } + + public VhdFooter CreateFooter() + { + try + { + ValidateVhdSize(); + var attributeHelper = new AttributeHelper(); + var footer = new VhdFooter(); + footer.Cookie = ReadVhdCookie(attributeHelper.GetAttribute(() => footer.Cookie)); + footer.Features = ReadFeatures(attributeHelper.GetAttribute(() => footer.Features)); + footer.FileFormatVersion = ReadVhdFileFormatVersion(attributeHelper.GetAttribute(() => footer.FileFormatVersion)); + footer.HeaderOffset = ReadHeaderOffset(attributeHelper.GetAttribute(() => footer.HeaderOffset)); + footer.TimeStamp = ReadTimeStamp(attributeHelper.GetAttribute(() => footer.TimeStamp)); + footer.CreatorApplication = ReadCreatorApplication(attributeHelper.GetAttribute(() => footer.CreatorApplication)); + footer.CreatorVersion = ReadCreatorVersion(attributeHelper.GetAttribute(() => footer.CreatorVersion)); + footer.CreatorHostOsType = ReadCreatorHostOsType(attributeHelper.GetAttribute(() => footer.CreatorHostOsType)); + footer.PhsyicalSize = ReadPhysicalSize(attributeHelper.GetAttribute(() => footer.PhsyicalSize)); + footer.VirtualSize = ReadVirtualSize(attributeHelper.GetAttribute(() => footer.VirtualSize)); + footer.DiskGeometry = ReadDiskGeometry(attributeHelper.GetAttribute(() => footer.DiskGeometry)); + footer.DiskType = ReadDiskType(attributeHelper.GetAttribute(() => footer.DiskType)); + footer.CheckSum = ReadCheckSum(attributeHelper.GetAttribute(() => footer.CheckSum)); + footer.UniqueId = ReadUniqueId(attributeHelper.GetAttribute(() => footer.UniqueId)); + footer.SavedState = ReadSavedState(attributeHelper.GetAttribute(() => footer.SavedState)); + footer.Reserved = ReadReserved(attributeHelper.GetAttribute(() => footer.Reserved)); + footer.RawData = ReadWholeFooter(attributeHelper.GetAttribute(() => footer.RawData)); + return footer; + } + catch (EndOfStreamException e) + { + throw new VhdParsingException("unsupported format", e); + } + } + + private T TryCatch(Func method, IAsyncResult result) + { + try + { + return method(result); + } + catch (EndOfStreamException e) + { + throw new VhdParsingException("unsupported format", e); + } + } + + public IAsyncResult BeginCreateFooter(AsyncCallback callback, object state) + { + return AsyncMachine.BeginAsyncMachine(CreateFooterAsync, callback, state); + } + + public VhdFooter EndCreateFooter(IAsyncResult result) + { + return AsyncMachine.EndAsyncMachine(result); + } + + private IEnumerable CreateFooterAsync(AsyncMachine machine) + { + ValidateVhdSize(); + var attributeHelper = new AttributeHelper(); + var footer = new VhdFooter(); + + BeginReadVhdCookie(attributeHelper.GetAttribute(() => footer.Cookie), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.Cookie = TryCatch(EndReadVhdCookie, machine.CompletionResult); + + BeginReadFeatures(attributeHelper.GetAttribute(() => footer.Features), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.Features = TryCatch(EndReadFeatures, machine.CompletionResult); + + BeginReadVhdFileFormatVersion(attributeHelper.GetAttribute(() => footer.FileFormatVersion), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.FileFormatVersion = TryCatch(EndReadVhdFileFormatVersion, machine.CompletionResult); + + BeginReadHeaderOffset(attributeHelper.GetAttribute(() => footer.HeaderOffset), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.HeaderOffset = TryCatch(EndReadHeaderOffset, machine.CompletionResult); + + BeginReadTimeStamp(attributeHelper.GetAttribute(() => footer.TimeStamp), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.TimeStamp = TryCatch(EndReadTimeStamp, machine.CompletionResult); + + BeginReadCreatorApplication(attributeHelper.GetAttribute(() => footer.CreatorApplication), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.CreatorApplication = TryCatch(EndReadCreatorApplication, machine.CompletionResult); + + BeginReadCreatorVersion(attributeHelper.GetAttribute(() => footer.CreatorVersion), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.CreatorVersion = TryCatch(EndReadCreatorVersion, machine.CompletionResult); + + BeginReadCreatorHostOsType(attributeHelper.GetAttribute(() => footer.CreatorHostOsType), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.CreatorHostOsType = TryCatch(EndReadCreatorHostOsType, machine.CompletionResult); + + BeginReadPhysicalSize(attributeHelper.GetAttribute(() => footer.PhsyicalSize), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.PhsyicalSize = TryCatch(EndReadPhysicalSize, machine.CompletionResult); + + BeginReadVirtualSize(attributeHelper.GetAttribute(() => footer.VirtualSize), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.VirtualSize = TryCatch(EndReadVirtualSize, machine.CompletionResult); + + BeginReadDiskGeometry(attributeHelper.GetAttribute(() => footer.DiskGeometry), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.DiskGeometry = TryCatch(EndReadDiskGeometry, machine.CompletionResult); + + BeginReadDiskType(attributeHelper.GetAttribute(() => footer.DiskType), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.DiskType = TryCatch(EndReadDiskType, machine.CompletionResult); + + BeginReadCheckSum(attributeHelper.GetAttribute(() => footer.CheckSum), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.CheckSum = TryCatch(EndReadCheckSum, machine.CompletionResult); + + BeginReadUniqueId(attributeHelper.GetAttribute(() => footer.UniqueId), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.UniqueId = TryCatch(EndReadUniqueId, machine.CompletionResult); + + BeginReadSavedState(attributeHelper.GetAttribute(() => footer.SavedState), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.SavedState = TryCatch(EndReadSavedState, machine.CompletionResult); + + BeginReadReserved(attributeHelper.GetAttribute(() => footer.Reserved), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.Reserved = TryCatch(EndReadReserved, machine.CompletionResult); + + BeginReadWholeFooter(attributeHelper.GetAttribute(() => footer.RawData), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + footer.RawData = TryCatch(EndReadWholeFooter, machine.CompletionResult); + + machine.ParameterValue = footer; + } + + private long ReadPhysicalSize(VhdPropertyAttribute attribute) + { + return (long)dataReader.ReadUInt64(this.GetFooterOffset() + attribute.Offset); + } + + private IAsyncResult BeginReadPhysicalSize(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt64(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private long EndReadPhysicalSize(IAsyncResult result) + { + var value = dataReader.EndReadUInt64(result); + return (long)value; + } + + private byte[] ReadWholeFooter(VhdPropertyAttribute attribute) + { + return dataReader.ReadBytes(GetFooterOffset() + attribute.Offset, attribute.Size); + } + + private IAsyncResult BeginReadWholeFooter(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadBytes(this.GetFooterOffset() + attribute.Offset, attribute.Size, callback, state); + } + + private byte[] EndReadWholeFooter(IAsyncResult result) + { + var value = dataReader.EndReadBytes(result); + return (byte[])value; + } + + private byte[] ReadReserved(VhdPropertyAttribute attribute) + { + return dataReader.ReadBytes(GetFooterOffset() + attribute.Offset, attribute.Size); + } + + private IAsyncResult BeginReadReserved(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadBytes(this.GetFooterOffset() + attribute.Offset, attribute.Size, callback, state); + } + + private byte[] EndReadReserved(IAsyncResult result) + { + var value = dataReader.EndReadBytes(result); + return (byte[])value; + } + + private bool ReadSavedState(VhdPropertyAttribute attribute) + { + return dataReader.ReadBoolean(GetFooterOffset() + attribute.Offset); + } + + private IAsyncResult BeginReadSavedState(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadBoolean(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private bool EndReadSavedState(IAsyncResult result) + { + var value = dataReader.EndReadBoolean(result); + return (bool)value; + } + + private uint ReadCheckSum(VhdPropertyAttribute attribute) + { + return dataReader.ReadUInt32(GetFooterOffset() + attribute.Offset); + } + + private IAsyncResult BeginReadCheckSum(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private uint EndReadCheckSum(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + return (uint)value; + } + + private DiskGeometry ReadDiskGeometry(VhdPropertyAttribute attribute) + { + long offset = GetFooterOffset() + attribute.Offset; + + var attributeHelper = new AttributeHelper(); + var diskGeometry = new DiskGeometry(); + diskGeometry.Cylinder = dataReader.ReadInt16(offset + attributeHelper.GetAttribute(() => diskGeometry.Cylinder).Offset); + diskGeometry.Heads = dataReader.ReadByte(offset + attributeHelper.GetAttribute(() => diskGeometry.Heads).Offset); + diskGeometry.Sectors = dataReader.ReadByte(offset + attributeHelper.GetAttribute(() => diskGeometry.Sectors).Offset); + return diskGeometry; + } + + private IAsyncResult BeginReadDiskGeometry(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return AsyncMachine.BeginAsyncMachine(ReadDiskGeometryAsync, attribute, callback, state); + } + + private DiskGeometry EndReadDiskGeometry(IAsyncResult result) + { + return AsyncMachine.EndAsyncMachine(result); + } + + private IEnumerable ReadDiskGeometryAsync(AsyncMachine machine, VhdPropertyAttribute attribute) + { + long offset = GetFooterOffset() + attribute.Offset; + + var attributeHelper = new AttributeHelper(); + var diskGeometry = new DiskGeometry(); + dataReader.BeginReadInt16(offset + attributeHelper.GetAttribute(() => diskGeometry.Cylinder).Offset, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + diskGeometry.Cylinder = dataReader.EndReadInt16(machine.CompletionResult); + + dataReader.BeginReadByte(offset + attributeHelper.GetAttribute(() => diskGeometry.Heads).Offset, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + diskGeometry.Heads = dataReader.EndReadByte(machine.CompletionResult); + + dataReader.BeginReadByte(offset + attributeHelper.GetAttribute(() => diskGeometry.Sectors).Offset, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + diskGeometry.Sectors = dataReader.EndReadByte(machine.CompletionResult); + + machine.ParameterValue = diskGeometry; + } + + private HostOsType ReadCreatorHostOsType(VhdPropertyAttribute attribute) + { + var hostOs = dataReader.ReadUInt32(GetFooterOffset() + attribute.Offset); + return (HostOsType)hostOs; + } + + private IAsyncResult BeginReadCreatorHostOsType(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private HostOsType EndReadCreatorHostOsType(IAsyncResult result) + { + var version = dataReader.EndReadUInt32(result); + return (HostOsType)version; + } + + private VhdCreatorVersion ReadCreatorVersion(VhdPropertyAttribute attribute) + { + var version = dataReader.ReadUInt32(GetFooterOffset() + attribute.Offset); + return new VhdCreatorVersion(version); + } + + private IAsyncResult BeginReadCreatorVersion(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private VhdCreatorVersion EndReadCreatorVersion(IAsyncResult result) + { + var version = dataReader.EndReadUInt32(result); + return new VhdCreatorVersion(version); + } + + private string ReadCreatorApplication(VhdPropertyAttribute attribute) + { + var creatorApplication = dataReader.ReadBytes(GetFooterOffset() + attribute.Offset, attribute.Size); + return Encoding.ASCII.GetString(creatorApplication); + } + + private IAsyncResult BeginReadCreatorApplication(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadBytes(this.GetFooterOffset() + attribute.Offset, attribute.Size, callback, state); + } + + private string EndReadCreatorApplication(IAsyncResult result) + { + var creatorApplication = dataReader.EndReadBytes(result); + return Encoding.ASCII.GetString(creatorApplication); + } + + private VhdFeature ReadFeatures(VhdPropertyAttribute attribute) + { + return (VhdFeature)dataReader.ReadUInt32(GetFooterOffset() + attribute.Offset); + } + + private IAsyncResult BeginReadFeatures(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private VhdFeature EndReadFeatures(IAsyncResult result) + { + return (VhdFeature)dataReader.EndReadUInt32(result); + } + + private Guid ReadUniqueId(VhdPropertyAttribute attribute) + { + return dataReader.ReadGuid(GetFooterOffset() + attribute.Offset); + } + + private IAsyncResult BeginReadUniqueId(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadGuid(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private Guid EndReadUniqueId(IAsyncResult result) + { + return dataReader.EndReadGuid(result); + } + + private DateTime ReadTimeStamp(VhdPropertyAttribute attribute) + { + return dataReader.ReadDateTime(GetFooterOffset() + attribute.Offset); + } + + private IAsyncResult BeginReadTimeStamp(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadDateTime(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private DateTime EndReadTimeStamp(IAsyncResult result) + { + return dataReader.EndReadDateTime(result); + } + + private long ReadHeaderOffset(VhdPropertyAttribute attribute) + { + return (long)dataReader.ReadUInt64(GetFooterOffset() + attribute.Offset); + } + + private IAsyncResult BeginReadHeaderOffset(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt64(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private long EndReadHeaderOffset(IAsyncResult result) + { + return (long)dataReader.EndReadUInt64(result); + } + + private DiskType ReadDiskType(VhdPropertyAttribute attribute) + { + var readDiskType = dataReader.ReadUInt32(GetFooterOffset() + attribute.Offset); + return diskTypeFactory.Create(readDiskType); + } + + private IAsyncResult BeginReadDiskType(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private DiskType EndReadDiskType(IAsyncResult result) + { + var readDiskType = dataReader.EndReadUInt32(result); + return diskTypeFactory.Create(readDiskType); + } + + private long ReadVirtualSize(VhdPropertyAttribute attribute) + { + return (long)dataReader.ReadUInt64(this.GetFooterOffset() + attribute.Offset); + } + + private IAsyncResult BeginReadVirtualSize(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt64(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private long EndReadVirtualSize(IAsyncResult result) + { + return (long)dataReader.EndReadUInt64(result); + } + + private void ValidateVhdSize() + { + // all VHDs are a multiple of 512 bytes. Note that Azure page blobs must + // be a multiple of 512 bytes also. + var streamLength = dataReader.Size; + if (streamLength == 0 || streamLength < VhdConstants.VHD_FOOTER_SIZE || streamLength % VhdConstants.VHD_PAGE_SIZE != 0) + throw new VhdParsingException(String.Format("Invalid file Size: {0}", streamLength)); + } + + private VhdFileFormatVersion ReadVhdFileFormatVersion(VhdPropertyAttribute attribute) + { + var version = dataReader.ReadUInt32(this.GetFooterOffset() + attribute.Offset); + return CreateVhdFileFormatVersion(version); + } + + private VhdFileFormatVersion CreateVhdFileFormatVersion(uint version) + { + var formatVersion = new VhdFileFormatVersion(version); + if (!formatVersion.IsSupported()) + throw new VhdParsingException(String.Format("Invalid file format version: {0}", formatVersion.Data)); + return formatVersion; + } + + private IAsyncResult BeginReadVhdFileFormatVersion(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(this.GetFooterOffset() + attribute.Offset, callback, state); + } + + private VhdFileFormatVersion EndReadVhdFileFormatVersion(IAsyncResult result) + { + uint value = dataReader.EndReadUInt32(result); + return CreateVhdFileFormatVersion(value); + } + + private VhdCookie ReadVhdCookie(VhdPropertyAttribute attribute) + { + byte[] value = dataReader.ReadBytes(this.GetFooterOffset() + attribute.Offset, attribute.Size); + return CreateVhdCookie(value); + } + + private VhdCookie CreateVhdCookie(byte[] cookie) + { + var vhdCookie = new VhdCookie(VhdCookieType.Footer, cookie); + if (!vhdCookie.IsValid()) + throw new VhdParsingException(String.Format("Invalid Vhd footer cookie:{0}", vhdCookie.StringData)); + return vhdCookie; + } + + private IAsyncResult BeginReadVhdCookie(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadBytes(this.GetFooterOffset() + attribute.Offset, attribute.Size, callback, state); + } + + private VhdCookie EndReadVhdCookie(IAsyncResult result) + { + byte[] cookie = dataReader.EndReadBytes(result); + return CreateVhdCookie(cookie); + } + + long GetFooterOffset() + { + return dataReader.Size - VhdConstants.VHD_FOOTER_SIZE; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFooterSerializer.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFooterSerializer.cs new file mode 100644 index 000000000000..21fbe5cf4e56 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdFooterSerializer.cs @@ -0,0 +1,87 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System.IO; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class VhdFooterSerializer + { + private readonly VhdFooter vhdFooter; + private AttributeHelper attributeHelper; + + public VhdFooterSerializer(VhdFooter vhdFooter) + { + this.vhdFooter = vhdFooter; + this.attributeHelper = new AttributeHelper(); + } + + public byte[] ToByteArray() + { + var buffer = new byte[attributeHelper.GetEntityAttribute().Size]; + using (var stream = new MemoryStream(buffer)) + { + var writer = new BinaryWriter(stream); + var dataWriter = new VhdDataWriter(writer); + dataWriter.WriteBytes(attributeHelper.GetAttribute(() => vhdFooter.Cookie).Offset, vhdFooter.Cookie.Data); + + dataWriter.WriteUInt(attributeHelper.GetAttribute(() => vhdFooter.Features).Offset, (uint)vhdFooter.Features); + dataWriter.WriteInt(attributeHelper.GetAttribute(() => vhdFooter.FileFormatVersion).Offset, + (int)vhdFooter.FileFormatVersion.Data); + dataWriter.WriteLong(attributeHelper.GetAttribute(() => vhdFooter.HeaderOffset).Offset, vhdFooter.HeaderOffset); + dataWriter.WriteTimeStamp(attributeHelper.GetAttribute(() => vhdFooter.TimeStamp).Offset, vhdFooter.TimeStamp); + dataWriter.WriteBytes(attributeHelper.GetAttribute(() => vhdFooter.CreatorApplication).Offset, + Encoding.ASCII.GetBytes(vhdFooter.CreatorApplication)); + dataWriter.WriteUInt(attributeHelper.GetAttribute(() => vhdFooter.CreatorVersion).Offset, + vhdFooter.CreatorVersion.Data); + dataWriter.WriteUInt(attributeHelper.GetAttribute(() => vhdFooter.CreatorHostOsType).Offset, + (uint)vhdFooter.CreatorHostOsType); + dataWriter.WriteLong(attributeHelper.GetAttribute(() => vhdFooter.PhsyicalSize).Offset, vhdFooter.PhsyicalSize); + dataWriter.WriteLong(attributeHelper.GetAttribute(() => vhdFooter.VirtualSize).Offset, vhdFooter.VirtualSize); + + dataWriter.SetPosition(attributeHelper.GetAttribute(() => vhdFooter.DiskGeometry).Offset); + WriteDiskGeometry(dataWriter, vhdFooter.DiskGeometry); + + dataWriter.WriteInt(attributeHelper.GetAttribute(() => vhdFooter.DiskType).Offset, (int)vhdFooter.DiskType); + dataWriter.WriteGuid(attributeHelper.GetAttribute(() => vhdFooter.UniqueId).Offset, vhdFooter.UniqueId); + dataWriter.WriteBoolean(attributeHelper.GetAttribute(() => vhdFooter.SavedState).Offset, vhdFooter.SavedState); + dataWriter.WriteBytes(attributeHelper.GetAttribute(() => vhdFooter.Reserved).Offset, vhdFooter.Reserved); + + dataWriter.WriteUInt(attributeHelper.GetAttribute(() => vhdFooter.CheckSum).Offset, ComputeCheckSum(buffer)); + } + return buffer; + } + + public uint ComputeCheckSum(byte[] buffer) + { + uint checksum = 0; + for (var i = 0; i < attributeHelper.GetEntityAttribute().Size; i++) + { + if (i < VhdConstants.VHD_FOOTER_OFFSET_CHECKSUM || i >= (VhdConstants.VHD_FOOTER_OFFSET_CHECKSUM + 4)) + { + checksum += buffer[i]; + } + } + return ~checksum; + } + + private static void WriteDiskGeometry(VhdDataWriter dataWriter, DiskGeometry diskGeometry) + { + dataWriter.WriteInt16(diskGeometry.Cylinder); + dataWriter.WriteByte(diskGeometry.Heads); + dataWriter.WriteByte(diskGeometry.Sectors); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdHeaderFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdHeaderFactory.cs new file mode 100644 index 000000000000..16b1267b7a35 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdHeaderFactory.cs @@ -0,0 +1,413 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Common.General; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class VhdHeaderFactory + { + private readonly VhdDataReader dataReader; + private readonly VhdFooter footer; + private long headerOffset; + + public VhdHeaderFactory(VhdDataReader dataReader, VhdFooter footer) + { + this.dataReader = dataReader; + this.footer = footer; + headerOffset = this.footer.HeaderOffset; + } + + public VhdHeader CreateHeader() + { + if (footer.DiskType != DiskType.Dynamic && footer.DiskType != DiskType.Differencing) + return null; + + try + { + var attributeHelper = new AttributeHelper(); + var header = new VhdHeader(); + header.Cookie = ReadHeaderCookie(attributeHelper.GetAttribute(() => header.Cookie)); + header.DataOffset = ReadDataOffset(attributeHelper.GetAttribute(() => header.DataOffset)); + header.TableOffset = ReadBATOffset(attributeHelper.GetAttribute(() => header.TableOffset)); + header.HeaderVersion = ReaderHeaderVersion(attributeHelper.GetAttribute(() => header.HeaderVersion)); + header.MaxTableEntries = ReadMaxTableEntries(attributeHelper.GetAttribute(() => header.MaxTableEntries)); + header.BlockSize = ReadBlockSize(attributeHelper.GetAttribute(() => header.BlockSize)); + header.CheckSum = ReadCheckSum(attributeHelper.GetAttribute(() => header.CheckSum)); + header.ParentUniqueId = ReadParentUniqueId(attributeHelper.GetAttribute(() => header.ParentUniqueId)); + header.ParentTimeStamp = ReadParentTimeStamp(attributeHelper.GetAttribute(() => header.ParentTimeStamp)); + header.Reserverd1 = ReadReserved1(attributeHelper.GetAttribute(() => header.Reserverd1)); + header.ParentPath = ReadParentPath(attributeHelper.GetAttribute(() => header.ParentPath)); + header.ParentLocators = ReadParentLocators(attributeHelper.GetAttribute(() => header.ParentLocators)); + header.RawData = ReadRawData(attributeHelper.GetAttribute(() => header.RawData)); + return header; + } + catch (EndOfStreamException e) + { + throw new VhdParsingException("unsupported format", e); + } + } + + private T TryCatch(Func method, IAsyncResult result) + { + try + { + return method(result); + } + catch (EndOfStreamException e) + { + throw new VhdParsingException("unsupported format", e); + } + } + + public IAsyncResult BeginCreateHeader(AsyncCallback callback, object state) + { + return AsyncMachine.BeginAsyncMachine(CreateVhdHeader, callback, state); + } + + public VhdHeader EndCreateHeader(IAsyncResult result) + { + return AsyncMachine.EndAsyncMachine(result); + } + + private IEnumerable CreateVhdHeader(AsyncMachine machine) + { + if (footer.DiskType != DiskType.Dynamic && footer.DiskType != DiskType.Differencing) + { + machine.ParameterValue = null; + yield break; + } + + var attributeHelper = new AttributeHelper(); + var header = new VhdHeader(); + + BeginReadHeaderCookie(attributeHelper.GetAttribute(() => header.Cookie), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.Cookie = TryCatch(EndReadHeaderCookie, machine.CompletionResult); + + BeginReadDataOffset(attributeHelper.GetAttribute(() => header.DataOffset), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.DataOffset = TryCatch(EndReadDataOffset, machine.CompletionResult); + + BeginReadBATOffset(attributeHelper.GetAttribute(() => header.TableOffset), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.TableOffset = TryCatch(EndReadBATOffset, machine.CompletionResult); + + BeginReadHeaderVersion(attributeHelper.GetAttribute(() => header.HeaderVersion), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.HeaderVersion = TryCatch(EndReadHeaderVersion, machine.CompletionResult); + + BeginReadMaxTableEntries(attributeHelper.GetAttribute(() => header.MaxTableEntries), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.MaxTableEntries = TryCatch(EndReadMaxTableEntries, machine.CompletionResult); + + BeginReadBlockSize(attributeHelper.GetAttribute(() => header.BlockSize), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.BlockSize = TryCatch(EndReadBlockSize, machine.CompletionResult); + + BeginReadCheckSum(attributeHelper.GetAttribute(() => header.CheckSum), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.CheckSum = TryCatch(EndReadCheckSum, machine.CompletionResult); + + BeginReadParentUniqueId(attributeHelper.GetAttribute(() => header.ParentUniqueId), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.ParentUniqueId = TryCatch(EndReadParentUniqueId, machine.CompletionResult); + + BeginReadParentTimeStamp(attributeHelper.GetAttribute(() => header.ParentTimeStamp), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.ParentTimeStamp = TryCatch(EndReadParentTimeStamp, machine.CompletionResult); + + BeginReadReserved1(attributeHelper.GetAttribute(() => header.Reserverd1), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.Reserverd1 = TryCatch(EndReadReserved1, machine.CompletionResult); + + BeginReadParentPath(attributeHelper.GetAttribute(() => header.ParentPath), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.ParentPath = TryCatch(EndReadParentPath, machine.CompletionResult); + + BeginReadParentLocators(attributeHelper.GetAttribute(() => header.ParentLocators), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.ParentLocators = TryCatch>(EndReadParentLocators, machine.CompletionResult); + + BeginReadRawData(attributeHelper.GetAttribute(() => header.RawData), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + header.RawData = TryCatch(EndReadRawData, machine.CompletionResult); + + machine.ParameterValue = header; + } + + private byte[] ReadRawData(VhdPropertyAttribute attribute) + { + return dataReader.ReadBytes(headerOffset + attribute.Offset, attribute.Size); + } + + private IAsyncResult BeginReadRawData(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadBytes(headerOffset + attribute.Offset, attribute.Size, callback, state); + } + + private byte[] EndReadRawData(IAsyncResult result) + { + var value = dataReader.EndReadBytes(result); + return (byte[])value; + } + + private IList ReadParentLocators(VhdPropertyAttribute attribute) + { + var parentLocators = new List(); + var attributeHelper = new AttributeHelper(); + var entityAttribute = attributeHelper.GetEntityAttribute(); + + long baseOffset = headerOffset + attribute.Offset; + + for (int i = 0; i < attribute.Count; i++) + { + var parentLocatorFactory = new VhdParentLocatorFactory(dataReader, baseOffset); + parentLocators.Add(parentLocatorFactory.Create()); + baseOffset += entityAttribute.Size; + } + return parentLocators; + } + + private IAsyncResult BeginReadParentLocators(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return AsyncMachine>.BeginAsyncMachine(CreateParentLocators, attribute, callback, state); + } + + private IEnumerable CreateParentLocators(AsyncMachine> machine, VhdPropertyAttribute attribute) + { + var parentLocators = new List(); + var attributeHelper = new AttributeHelper(); + var entityAttribute = attributeHelper.GetEntityAttribute(); + + long baseOffset = headerOffset + attribute.Offset; + + for (int i = 0; i < attribute.Count; i++) + { + var parentLocatorFactory = new VhdParentLocatorFactory(dataReader, baseOffset); + + parentLocatorFactory.BeginReadCreate(machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + ParentLocator parentLocator = parentLocatorFactory.EndReadCreate(machine.CompletionResult); + + parentLocators.Add(parentLocator); + baseOffset += entityAttribute.Size; + } + machine.ParameterValue = parentLocators; + } + + private IList EndReadParentLocators(IAsyncResult result) + { + return AsyncMachine>.EndAsyncMachine(result); + } + + private uint ReadReserved1(VhdPropertyAttribute attribute) + { + return dataReader.ReadUInt32(headerOffset + attribute.Offset); + } + + private IAsyncResult BeginReadReserved1(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(headerOffset + attribute.Offset, callback, state); + } + + private uint EndReadReserved1(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + return (uint)value; + } + + private uint ReadCheckSum(VhdPropertyAttribute attribute) + { + return dataReader.ReadUInt32(headerOffset + attribute.Offset); + } + + private IAsyncResult BeginReadCheckSum(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(headerOffset + attribute.Offset, callback, state); + } + + private uint EndReadCheckSum(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + return (uint)value; + } + + private long ReadDataOffset(VhdPropertyAttribute attribute) + { + return (long)dataReader.ReadUInt64(headerOffset + attribute.Offset); + } + + private IAsyncResult BeginReadDataOffset(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt64(headerOffset + attribute.Offset, callback, state); + } + + private long EndReadDataOffset(IAsyncResult result) + { + var value = dataReader.EndReadUInt64(result); + return (long)value; + } + + + private string ReadParentPath(VhdPropertyAttribute attribute) + { + var parentNameBytes = dataReader.ReadBytes(headerOffset + attribute.Offset, attribute.Size); + return Encoding.BigEndianUnicode.GetString(parentNameBytes).TrimEnd('\0'); + } + + private IAsyncResult BeginReadParentPath(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadBytes(headerOffset + attribute.Offset, attribute.Size, callback, state); + } + + private string EndReadParentPath(IAsyncResult result) + { + var value = dataReader.EndReadBytes(result); + return Encoding.BigEndianUnicode.GetString(value).TrimEnd('\0'); + } + + private DateTime ReadParentTimeStamp(VhdPropertyAttribute attribute) + { + return dataReader.ReadDateTime(headerOffset + attribute.Offset); + } + + private IAsyncResult BeginReadParentTimeStamp(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadDateTime(headerOffset + attribute.Offset, callback, state); + } + + private DateTime EndReadParentTimeStamp(IAsyncResult result) + { + var value = dataReader.EndReadDateTime(result); + return (DateTime)value; + } + + private Guid ReadParentUniqueId(VhdPropertyAttribute attribute) + { + return dataReader.ReadGuid(headerOffset + attribute.Offset); + } + + private IAsyncResult BeginReadParentUniqueId(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadGuid(headerOffset + attribute.Offset, callback, state); + } + + private Guid EndReadParentUniqueId(IAsyncResult result) + { + var value = dataReader.EndReadGuid(result); + return (Guid)value; + } + + private uint ReadBlockSize(VhdPropertyAttribute attribute) + { + return dataReader.ReadUInt32(headerOffset + attribute.Offset); + } + + private IAsyncResult BeginReadBlockSize(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(headerOffset + attribute.Offset, callback, state); + } + + private uint EndReadBlockSize(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + return (uint)value; + } + + private uint ReadMaxTableEntries(VhdPropertyAttribute attribute) + { + return dataReader.ReadUInt32(headerOffset + attribute.Offset); + } + + private IAsyncResult BeginReadMaxTableEntries(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(headerOffset + attribute.Offset, callback, state); + } + + private uint EndReadMaxTableEntries(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + return (uint)value; + } + + private long ReadBATOffset(VhdPropertyAttribute attribute) + { + return (long)dataReader.ReadUInt64(headerOffset + attribute.Offset); + } + + private IAsyncResult BeginReadBATOffset(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt64(headerOffset + attribute.Offset, callback, state); + } + + private long EndReadBATOffset(IAsyncResult result) + { + var value = dataReader.EndReadUInt64(result); + return (long)value; + } + + private VhdHeaderVersion ReaderHeaderVersion(VhdPropertyAttribute attribute) + { + var version = dataReader.ReadUInt32(headerOffset + attribute.Offset); + var headerVersion = new VhdHeaderVersion(version); + if (!headerVersion.IsSupported()) + throw new VhdParsingException("unsupported format"); + return headerVersion; + } + + private IAsyncResult BeginReadHeaderVersion(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(headerOffset + attribute.Offset, callback, state); + } + + private VhdHeaderVersion EndReadHeaderVersion(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + var headerVersion = new VhdHeaderVersion(value); + if (!headerVersion.IsSupported()) + throw new VhdParsingException("unsupported format"); + return headerVersion; + } + + private VhdCookie ReadHeaderCookie(VhdPropertyAttribute attribute) + { + var cookie = dataReader.ReadBytes(headerOffset + attribute.Offset, attribute.Size); + var vhdCookie = new VhdCookie(VhdCookieType.Header, cookie); + if (!vhdCookie.IsValid()) + throw new VhdParsingException(String.Format("unsupported format, Cookie:{0}", vhdCookie)); + return vhdCookie; + } + + private IAsyncResult BeginReadHeaderCookie(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadBytes(headerOffset + attribute.Offset, attribute.Size, callback, state); + } + + private VhdCookie EndReadHeaderCookie(IAsyncResult result) + { + var value = dataReader.EndReadBytes(result); + var vhdCookie = new VhdCookie(VhdCookieType.Header, value); + if (!vhdCookie.IsValid()) + throw new VhdParsingException(String.Format("unsupported format, Cookie:{0}", vhdCookie)); + return vhdCookie; + } + + + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdParentLocatorFactory.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdParentLocatorFactory.cs new file mode 100644 index 000000000000..13d2bb780803 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Persistence/VhdParentLocatorFactory.cs @@ -0,0 +1,210 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Common.General; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence +{ + public class VhdParentLocatorFactory + { + private readonly VhdDataReader dataReader; + private readonly long offset; + private AttributeHelper attributeHelper; + + public VhdParentLocatorFactory(VhdDataReader dataReader, long offset) + { + this.dataReader = dataReader; + this.offset = offset; + attributeHelper = new AttributeHelper(); + } + + public ParentLocator Create() + { + var locator = new ParentLocator(); + locator.PlatformCode = ReadPlaformCode(attributeHelper.GetAttribute(() => locator.PlatformCode)); + locator.PlatformDataSpace = ReadPlatformDataSpace(attributeHelper.GetAttribute(() => locator.PlatformDataSpace)); + locator.PlatformDataLength = ReadPlatformDataLength(attributeHelper.GetAttribute(() => locator.PlatformDataLength)); + locator.Reserved = ReadReserved(attributeHelper.GetAttribute(() => locator.Reserved)); + locator.PlatformDataOffset = ReadPlatformDataOffset(attributeHelper.GetAttribute(() => locator.PlatformDataOffset)); + locator.PlatformSpecificFileLocator = ReadFileLocator(locator); + return locator; + } + + public IAsyncResult BeginReadCreate(AsyncCallback callback, object state) + { + return AsyncMachine.BeginAsyncMachine(CreateParentLocator, callback, state); + } + + public ParentLocator EndReadCreate(IAsyncResult result) + { + return AsyncMachine.EndAsyncMachine(result); + } + + private IEnumerable CreateParentLocator(AsyncMachine machine) + { + var locator = new ParentLocator(); + + BeginReadPlatformCode(attributeHelper.GetAttribute(() => locator.PlatformCode), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + locator.PlatformCode = EndReadPlatformCode(machine.CompletionResult); + + BeginReadPlatformDataSpace(attributeHelper.GetAttribute(() => locator.PlatformDataSpace), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + locator.PlatformDataSpace = EndReadPlatformDataSpace(machine.CompletionResult); + + BeginReadPlatformDataLength(attributeHelper.GetAttribute(() => locator.PlatformDataLength), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + locator.PlatformDataLength = EndReadPlatformDataLength(machine.CompletionResult); + + BeginReadReserved(attributeHelper.GetAttribute(() => locator.Reserved), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + locator.Reserved = EndReadReserved(machine.CompletionResult); + + BeginReadPlatformDataOffset(attributeHelper.GetAttribute(() => locator.PlatformDataOffset), machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + locator.PlatformDataOffset = EndReadPlatformDataOffset(machine.CompletionResult); + + BeginReadFileLocator(locator, machine.CompletionCallback, null); + yield return CompletionPort.SingleOperation; + locator.PlatformSpecificFileLocator = EndReadFileLocator(machine.CompletionResult); + + machine.ParameterValue = locator; + } + + private string ReadFileLocator(ParentLocator locator) + { + var fileLocator = dataReader.ReadBytes(locator.PlatformDataOffset, locator.PlatformDataLength); + return CreateFileLocator(locator, fileLocator); + } + + private string CreateFileLocator(ParentLocator locator, byte[] fileLocator) + { + switch (locator.PlatformCode) + { + case PlatformCode.None: + return String.Empty; + case PlatformCode.Wi2R: + case PlatformCode.Wi2K: + throw new VhdParsingException(String.Format("Deprecated PlatformCode:{0}", locator.PlatformCode)); + case PlatformCode.W2Ru: + //TODO: Add differencing disks path name, this is relative path + return Encoding.Unicode.GetString(fileLocator); + case PlatformCode.W2Ku: + return Encoding.Unicode.GetString(fileLocator); + case PlatformCode.Mac: + //TODO: Mac OS alias stored as a blob? + throw new NotImplementedException(String.Format("PlatformCode: {0}", locator.PlatformCode)); + case PlatformCode.MacX: + return Encoding.UTF8.GetString(fileLocator); + } + return Encoding.BigEndianUnicode.GetString(fileLocator).TrimEnd('\0'); + } + + private IAsyncResult BeginReadFileLocator(ParentLocator locator, AsyncCallback callback, object state) + { + return dataReader.BeginReadBytes(locator.PlatformDataOffset, locator.PlatformDataLength, callback, locator); + } + + private string EndReadFileLocator(IAsyncResult result) + { + var fileLocator = dataReader.EndReadBytes(result); + var locator = (ParentLocator)result.AsyncState; + return CreateFileLocator(locator, fileLocator); + } + + private PlatformCode ReadPlaformCode(VhdPropertyAttribute attribute) + { + return (PlatformCode)dataReader.ReadUInt32(offset + attribute.Offset); + } + + private IAsyncResult BeginReadPlatformCode(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(offset + attribute.Offset, callback, state); + } + + private PlatformCode EndReadPlatformCode(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + return (PlatformCode)value; + } + + private int ReadPlatformDataSpace(VhdPropertyAttribute attribute) + { + return (int)dataReader.ReadUInt32(offset + attribute.Offset); + } + + private IAsyncResult BeginReadPlatformDataSpace(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(offset + attribute.Offset, callback, state); + } + + private int EndReadPlatformDataSpace(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + return (int)value; + } + + private int ReadPlatformDataLength(VhdPropertyAttribute attribute) + { + return (int)dataReader.ReadUInt32(offset + attribute.Offset); + } + + private IAsyncResult BeginReadPlatformDataLength(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(offset + attribute.Offset, callback, state); + } + + private int EndReadPlatformDataLength(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + return (int)value; + } + + private int ReadReserved(VhdPropertyAttribute attribute) + { + return (int)dataReader.ReadUInt32(offset + attribute.Offset); + } + + private IAsyncResult BeginReadReserved(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt32(offset + attribute.Offset, callback, state); + } + + private int EndReadReserved(IAsyncResult result) + { + var value = dataReader.EndReadUInt32(result); + return (int)value; + } + + private long ReadPlatformDataOffset(VhdPropertyAttribute attribute) + { + return (long)dataReader.ReadUInt64(offset + attribute.Offset); + } + + private IAsyncResult BeginReadPlatformDataOffset(VhdPropertyAttribute attribute, AsyncCallback callback, object state) + { + return dataReader.BeginReadUInt64(offset + attribute.Offset, callback, state); + } + + private long EndReadPlatformDataOffset(IAsyncResult result) + { + var value = dataReader.EndReadUInt64(result); + return (long)value; + } + + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/PlatformCode.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/PlatformCode.cs new file mode 100644 index 000000000000..b28d3191d2d2 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/PlatformCode.cs @@ -0,0 +1,27 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public enum PlatformCode + { + None = 0x0, + Wi2R = 0x57693272, + Wi2K = 0x5769326B, + W2Ru = 0x57327275, + W2Ku = 0x57326B75, + Mac = 0x4D616320, + MacX = 0x4D616358 + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Sector.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Sector.cs new file mode 100644 index 000000000000..6ef4f41d8f90 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/Sector.cs @@ -0,0 +1,25 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class Sector + { + public uint BlockIndex { get; set; } + public long SectorIndex { get; set; } + public byte[] Data { get; set; } + public IndexRange LogicalRange { get; set; } + public long GlobalSectorIndex { get; set; } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCookie.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCookie.cs new file mode 100644 index 000000000000..7deeb2025dff --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCookie.cs @@ -0,0 +1,79 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Linq; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class VhdCookie + { + static readonly byte[] FooterCookie = Encoding.ASCII.GetBytes("conectix"); + static readonly byte[] HeaderCookie = Encoding.ASCII.GetBytes("cxsparse"); + + private readonly VhdCookieType cookieType; + private readonly byte[] expectedData; + + public static VhdCookie CreateFooterCookie() + { + return new VhdCookie(VhdCookieType.Footer, FooterCookie); + } + + public static VhdCookie CreateHeaderCookie() + { + return new VhdCookie(VhdCookieType.Header, HeaderCookie); + } + + public VhdCookie(VhdCookieType cookieType, byte[] data) + { + this.cookieType = cookieType; + this.Data = data; + this.expectedData = GetExpectedCookie(); + } + + public byte[] Data { get; private set; } + + public string StringData + { + get { return Encoding.ASCII.GetString(this.Data); } + } + + public bool IsValid() + { + if (Data.Length != expectedData.Length) + { + return false; + } + return !expectedData.Where((t, i) => Data[i] != t).Any(); + } + + private byte[] GetExpectedCookie() + { + return cookieType == VhdCookieType.Header ? HeaderCookie : FooterCookie; + } + + public VhdCookie CreateCopy() + { + var copy = new byte[Data.Length]; + Array.Copy(Data, copy, Data.Length); + return new VhdCookie(this.cookieType, copy); + } + + public override string ToString() + { + return StringData; + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCookieType.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCookieType.cs new file mode 100644 index 000000000000..ca54a12b924d --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCookieType.cs @@ -0,0 +1,22 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public enum VhdCookieType + { + Header, + Footer + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCreatorVersion.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCreatorVersion.cs new file mode 100644 index 000000000000..2327344fdee7 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdCreatorVersion.cs @@ -0,0 +1,35 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class VhdCreatorVersion + { + public static VhdCreatorVersion VS2004 = new VhdCreatorVersion(0x00010000); + public static VhdCreatorVersion VPC2004 = new VhdCreatorVersion(0x00050000); + public static VhdCreatorVersion CSUP2011 = new VhdCreatorVersion(0x00070000); + + public VhdCreatorVersion(uint data) + { + this.Data = data; + } + + public uint Data { get; private set; } + + public override string ToString() + { + return this.Data.ToString(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdEntityAttribute.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdEntityAttribute.cs new file mode 100644 index 000000000000..ab428746b70c --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdEntityAttribute.cs @@ -0,0 +1,23 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class VhdEntityAttribute : Attribute + { + public int Size { get; set; } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdEntityDescriptor.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdEntityDescriptor.cs new file mode 100644 index 000000000000..9394b81c41e4 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdEntityDescriptor.cs @@ -0,0 +1,46 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class VhdEntityDescriptor + { + public VhdEntityDescriptor() + { + this.PropertyDescriptors = GetPropertyDescriptors(); + } + + public IList PropertyDescriptors { get; private set; } + + static IList GetPropertyDescriptors() + { + return (from p in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public) + let vhdPropertyAttributes = p.GetCustomAttributes(typeof(VhdPropertyAttribute), false) + let exists = vhdPropertyAttributes.Length > 0 + let getter = p.GetGetMethod(true) + let setter = p.GetSetMethod(true) + where exists + select new VhdPropertyDescriptor + { + Attribute = (VhdPropertyAttribute)(vhdPropertyAttributes[0]), + Getter = getter, + Setter = setter + }).ToList(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFeature.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFeature.cs new file mode 100644 index 000000000000..be79f7dce86b --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFeature.cs @@ -0,0 +1,26 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + [Flags] + public enum VhdFeature : uint + { + NoFeaturesEnabled = 0x00000000, + Temporary = 0x00000001, + Reserved = 0x00000002 + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFile.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFile.cs new file mode 100644 index 000000000000..81b4acb7c486 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFile.cs @@ -0,0 +1,106 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class VhdFile : IDisposable + { + private BinaryReader reader; + private bool disposed; + + public VhdFile(VhdFooter footer, VhdHeader header, BlockAllocationTable bat, VhdFile parent, Stream stream) + { + this.Footer = footer; + this.Header = header; + this.BlockAllocationTable = bat; + this.Parent = parent; + this.reader = new BinaryReader(stream, Encoding.Unicode); + DataReader = new VhdDataReader(this.reader); + } + + // These properties, and all others on this object, must remain immutable for certain FxCop suppressions to be allowed. + // If at any time this class is changed, please revisit all suppressions of the DoNotDeclareReadOnlyMutableReferenceTypes + // rule where VhdFile is declared as a readonly object. Otherwise it is always safe to suppress the rule DoNotDeclareReadOnlyMutableReferenceTypes + public VhdDataReader DataReader { get; private set; } + public VhdFooter Footer { get; private set; } + public VhdHeader Header { get; private set; } + public BlockAllocationTable BlockAllocationTable { get; private set; } + public DiskType DiskType { get { return Footer.DiskType; } } + public VhdFile Parent { get; private set; } + + public IEnumerable GetBlocks() + { + var blockFactory = this.GetBlockFactory(); + for (long index = 0; index < blockFactory.BlockCount; index++) + { + yield return blockFactory.Create((uint)index); + } + } + + public IBlockFactory GetBlockFactory() + { + switch (this.DiskType) + { + case DiskType.Fixed: + return new FixedDiskBlockFactory(this); + case DiskType.Dynamic: + return new DynamicDiskBlockFactory(this); + case DiskType.Differencing: + return new DifferencingDiskBlockFactory(this); + default: + throw new InvalidOperationException(String.Format("Unsupported DiskType:{0}", this.DiskType)); + } + } + + public IEnumerable GetIdentityChain() + { + var identities = new List { this.Footer.UniqueId }; + var currentFile = this.Parent; + while (currentFile != null) + { + identities.Add(currentFile.Footer.UniqueId); + currentFile = currentFile.Parent; + } + return identities; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + this.reader.Close(); + } + if (Parent != null) + { + Parent.Dispose(); + } + disposed = true; + } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFileFormatVersion.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFileFormatVersion.cs new file mode 100644 index 000000000000..1850a98e2a69 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFileFormatVersion.cs @@ -0,0 +1,55 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + + public class VhdFileFormatVersion + { + public static VhdFileFormatVersion DefaultFileFormatVersion = + new VhdFileFormatVersion((int)VHD_FOOTER_SUPPORTED_VERSION); + + const uint VHD_FOOTER_SUPPORTED_VERSION = 0x00010000; + + public VhdFileFormatVersion(uint data) + { + this.Data = data; + } + + public uint Data { get; private set; } + + public bool IsSupported() + { + return Data == VHD_FOOTER_SUPPORTED_VERSION; + } + + public override string ToString() + { + return this.Data.ToString(); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != typeof(VhdFileFormatVersion)) return false; + return ((VhdFileFormatVersion)obj).Data == Data; + } + + public override int GetHashCode() + { + return Data.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFooter.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFooter.cs new file mode 100644 index 000000000000..1063c9363469 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdFooter.cs @@ -0,0 +1,141 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + [VhdEntity(Size = 512)] + public class VhdFooter + { + [VhdProperty(Offset = 0, Size = 8)] + public VhdCookie Cookie { get; set; } + + [VhdProperty(Offset = 8, Size = 4)] + public VhdFeature Features { get; set; } + + [VhdProperty(Offset = 12, Size = 4)] + public VhdFileFormatVersion FileFormatVersion { get; set; } + + [VhdProperty(Offset = 16, Size = 8)] + public long HeaderOffset { get; set; } + + [VhdProperty(Offset = 24, Size = 4)] + public DateTime TimeStamp { get; set; } + + [VhdProperty(Offset = 28, Size = 4)] + public string CreatorApplication { get; set; } + + [VhdProperty(Offset = 32, Size = 4)] + public VhdCreatorVersion CreatorVersion { get; set; } + + [VhdProperty(Offset = 36, Size = 4)] + public HostOsType CreatorHostOsType { get; set; } + + [VhdProperty(Offset = 40, Size = 8)] + public long PhsyicalSize { get; set; } + + [VhdProperty(Offset = 48, Size = 8)] + public long VirtualSize { get; set; } + + [VhdProperty(Offset = 56, Size = 4)] + public DiskGeometry DiskGeometry { get; set; } + + [VhdProperty(Offset = 60, Size = 4)] + public DiskType DiskType { get; set; } + + [VhdProperty(Offset = 64, Size = 4)] + public uint CheckSum { get; set; } + + [VhdProperty(Offset = 68, Size = 16)] + public Guid UniqueId { get; set; } + + [VhdProperty(Offset = 84, Size = 1)] + public bool SavedState { get; set; } + + [VhdProperty(Offset = 85, Size = 427)] + public byte[] Reserved { get; set; } + + [VhdProperty(Offset = 0, Size = 512)] + public byte[] RawData { get; set; } + + public VhdFooter CreateCopy() + { + return new VhdFooter + { + Cookie = this.Cookie.CreateCopy(), + Features = this.Features, + FileFormatVersion = this.FileFormatVersion, + HeaderOffset = this.HeaderOffset, + TimeStamp = this.TimeStamp, + CreatorApplication = this.CreatorApplication, + CreatorVersion = this.CreatorVersion, + CreatorHostOsType = this.CreatorHostOsType, + PhsyicalSize = this.PhsyicalSize, + VirtualSize = this.VirtualSize, + DiskGeometry = this.DiskGeometry.CreateCopy(), + DiskType = this.DiskType, + CheckSum = this.CheckSum, + UniqueId = this.UniqueId, + SavedState = this.SavedState, + Reserved = CreateCopy(this.Reserved), + RawData = CreateCopy(this.RawData), + }; + } + + static byte[] CreateCopy(byte[] data) + { + var result = new byte[data.Length]; + Array.Copy(data, result, data.Length); + return result; + } + + public override bool Equals(object obj) + { + return Equals(obj as VhdFooter); + } + + private bool Equals(VhdFooter other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(other.Cookie, Cookie) && Equals(other.Features, Features) && Equals(other.FileFormatVersion, FileFormatVersion) && other.HeaderOffset == HeaderOffset && other.TimeStamp.Equals(TimeStamp) && Equals(other.CreatorApplication, CreatorApplication) && Equals(other.CreatorVersion, CreatorVersion) && Equals(other.CreatorHostOsType, CreatorHostOsType) && other.PhsyicalSize == PhsyicalSize && other.VirtualSize == VirtualSize && Equals(other.DiskGeometry, DiskGeometry) && Equals(other.DiskType, DiskType) && other.CheckSum == CheckSum && other.UniqueId.Equals(UniqueId) && other.SavedState.Equals(SavedState) && Equals(other.Reserved, Reserved) && Equals(other.RawData, RawData); + } + + public override int GetHashCode() + { + unchecked + { + int result = (Cookie != null ? Cookie.GetHashCode() : 0); + result = (result * 397) ^ Features.GetHashCode(); + result = (result * 397) ^ (FileFormatVersion != null ? FileFormatVersion.GetHashCode() : 0); + result = (result * 397) ^ HeaderOffset.GetHashCode(); + result = (result * 397) ^ TimeStamp.GetHashCode(); + result = (result * 397) ^ (CreatorApplication != null ? CreatorApplication.GetHashCode() : 0); + result = (result * 397) ^ (CreatorVersion != null ? CreatorVersion.GetHashCode() : 0); + result = (result * 397) ^ CreatorHostOsType.GetHashCode(); + result = (result * 397) ^ PhsyicalSize.GetHashCode(); + result = (result * 397) ^ VirtualSize.GetHashCode(); + result = (result * 397) ^ (DiskGeometry != null ? DiskGeometry.GetHashCode() : 0); + result = (result * 397) ^ DiskType.GetHashCode(); + result = (result * 397) ^ CheckSum.GetHashCode(); + result = (result * 397) ^ UniqueId.GetHashCode(); + result = (result * 397) ^ SavedState.GetHashCode(); + result = (result * 397) ^ (Reserved != null ? Reserved.GetHashCode() : 0); + result = (result * 397) ^ (RawData != null ? RawData.GetHashCode() : 0); + return result; + } + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdHeader.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdHeader.cs new file mode 100644 index 000000000000..0feafa35d951 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdHeader.cs @@ -0,0 +1,77 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + [VhdEntity(Size = 1024)] + public class VhdHeader + { + [VhdProperty(Offset = 0, Size = 8)] + public VhdCookie Cookie { get; set; } + + [VhdProperty(Offset = 8, Size = 8)] + public long DataOffset { get; set; } + + [VhdProperty(Offset = 16, Size = 8)] + public long TableOffset { get; set; } + + [VhdProperty(Offset = 24, Size = 4)] + public VhdHeaderVersion HeaderVersion { get; set; } + + [VhdProperty(Offset = 28, Size = 4)] + public uint MaxTableEntries { get; set; } + + [VhdProperty(Offset = 32, Size = 4)] + public uint BlockSize { get; set; } + + [VhdProperty(Offset = 36, Size = 4)] + public uint CheckSum { get; set; } + + [VhdProperty(Offset = 40, Size = 16)] + public Guid ParentUniqueId { get; set; } + + [VhdProperty(Offset = 56, Size = 4)] + public DateTime ParentTimeStamp { get; set; } + + [VhdProperty(Offset = 60, Size = 4)] + public uint Reserverd1 { get; set; } + + [VhdProperty(Offset = 64, Size = 512)] + public string ParentPath { get; set; } + + [VhdProperty(Offset = 576, Count = 8)] + public IList ParentLocators { get; set; } + + [VhdProperty(Offset = 0, Size = 1024)] + public byte[] RawData { get; set; } + + public string GetAbsoluteParentPath() + { + return (from p in ParentLocators + where p.PlatformCode == PlatformCode.W2Ku + select p.PlatformSpecificFileLocator).FirstOrDefault(); + } + + public string GetRelativeParentPath() + { + return (from p in ParentLocators + where p.PlatformCode == PlatformCode.W2Ru + select p.PlatformSpecificFileLocator).FirstOrDefault(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdHeaderVersion.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdHeaderVersion.cs new file mode 100644 index 000000000000..5b3731986fd9 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdHeaderVersion.cs @@ -0,0 +1,38 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class VhdHeaderVersion + { + private const uint VHD_HEADER_SUPPORTED_VERSION = 0x00010000; + + public VhdHeaderVersion(uint data) + { + this.Data = data; + } + + public uint Data { get; private set; } + + public bool IsSupported() + { + return Data == VHD_HEADER_SUPPORTED_VERSION; + } + + public override string ToString() + { + return this.Data.ToString(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdPropertyAttribute.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdPropertyAttribute.cs new file mode 100644 index 000000000000..c57d15cc6446 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdPropertyAttribute.cs @@ -0,0 +1,25 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class VhdPropertyAttribute : Attribute + { + public int Offset { get; set; } + public int Size { get; set; } + public int Count { get; set; } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdPropertyDescriptor.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdPropertyDescriptor.cs new file mode 100644 index 000000000000..13a758e8f08b --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdPropertyDescriptor.cs @@ -0,0 +1,27 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System.Reflection; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class VhdPropertyDescriptor + { + public VhdPropertyAttribute Attribute { get; set; } + + public MethodInfo Getter { get; set; } + + public MethodInfo Setter { get; set; } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdTimeStamp.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdTimeStamp.cs new file mode 100644 index 000000000000..b3f7d1702a9f --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/Model/VhdTimeStamp.cs @@ -0,0 +1,47 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd.Model +{ + public class VhdTimeStamp + { + private static readonly DateTime VhdBaseTime = new DateTime(2000, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + private readonly uint value; + + public VhdTimeStamp(DateTime dateTime) + { + if (dateTime < VhdBaseTime) + { + var message = String.Format("DateTime must be after Base Vhd Time: {0}", VhdBaseTime); + throw new ArgumentOutOfRangeException("dateTime", message); + } + + this.TotalSeconds = (uint)dateTime.Subtract(VhdBaseTime).TotalSeconds; + } + + public VhdTimeStamp(uint value) + { + this.value = value; + } + + public uint TotalSeconds { get; private set; } + + public DateTime ToDateTime() + { + return VhdBaseTime.AddSeconds(value).ToUniversalTime(); + } + } +} \ No newline at end of file diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/SparseStream.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/SparseStream.cs new file mode 100644 index 000000000000..a557a0d09196 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/SparseStream.cs @@ -0,0 +1,54 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd +{ + ///

+ /// A stream with additional extent information. + /// + /// + /// Extent information reveals the ranges of the stream that contain non-zero data. Clients may use + /// the extent information to optimize reads to the stream. The stream supports reads over any range, + /// regardless of the extents. + /// + public abstract class SparseStream : Stream + { + public abstract IEnumerable Extents { get; } + } + + /// + /// An extent. + /// + public struct StreamExtent + { + public Guid Owner; + public long StartOffset; + public long EndOffset; + + public long Length + { + get + { + return (this.EndOffset - this.StartOffset) + 1; + } + } + + public IndexRange Range { get; set; } + } +} diff --git a/src/ResourceManager/Compute/Commands.Compute/VhdManagement/VirtualDiskStream.cs b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/VirtualDiskStream.cs new file mode 100644 index 000000000000..3b19fd4886c4 --- /dev/null +++ b/src/ResourceManager/Compute/Commands.Compute/VhdManagement/VirtualDiskStream.cs @@ -0,0 +1,257 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model; +using Microsoft.WindowsAzure.Commands.Tools.Vhd.Model.Persistence; +using System; +using System.Collections.Generic; +using System.IO; + +namespace Microsoft.WindowsAzure.Commands.Tools.Vhd +{ + /// + /// Provides a logical stream over a virtual hard disk (VHD). + /// + /// + /// This stream implementation provides a "view" over a VHD, such that the + /// VHD appears to be an ordinary fixed VHD file, regardless of the true physical layout. + /// This stream supports any combination of differencing, dynamic disks, and fixed disks. + /// + public class VirtualDiskStream : SparseStream + { + private long position; + private VhdFile vhdFile; + private IBlockFactory blockFactory; + private IndexRange footerRange; + private IndexRange fileDataRange; + private bool isDisposed; + + public VirtualDiskStream(string vhdPath) + { + this.vhdFile = new VhdFileFactory().Create(vhdPath); + this.blockFactory = vhdFile.GetBlockFactory(); + footerRange = this.blockFactory.GetFooterRange(); + fileDataRange = IndexRange.FromLength(0, this.Length - footerRange.Length); + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return true; } + } + + public override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + } + + public override sealed long Length + { + get { return this.footerRange.EndIndex + 1; } + } + + public override long Position + { + get { return this.position; } + set + { + if (value < 0) throw new ArgumentException(); + if (value >= this.Length) throw new EndOfStreamException(); + this.position = value; + } + } + + /// + /// Gets the extents of the stream that contain data. + /// + public override IEnumerable Extents + { + get + { + for (uint index = 0; index < blockFactory.BlockCount; index++) + { + var block = blockFactory.Create(index); + if (!block.Empty) + { + yield return new StreamExtent + { + Owner = block.VhdUniqueId, + StartOffset = block.LogicalRange.StartIndex, + EndOffset = block.LogicalRange.EndIndex, + Range = block.LogicalRange + }; + } + } + yield return new StreamExtent + { + Owner = vhdFile.Footer.UniqueId, + StartOffset = this.footerRange.StartIndex, + EndOffset = this.footerRange.EndIndex, + Range = this.footerRange + }; + } + } + + public DiskType DiskType + { + get { return this.vhdFile.DiskType; } + } + + public DiskType RootDiskType + { + get + { + var diskType = this.vhdFile.DiskType; + for (var parent = this.vhdFile.Parent; parent != null; parent = parent.Parent) + { + diskType = parent.DiskType; + } + return diskType; + } + } + /// + /// Reads the specified number of bytes from the current position. + /// + public override int Read(byte[] buffer, int offset, int count) + { + if (count <= 0) + { + return 0; + } + + try + { + var rangeToRead = IndexRange.FromLength(this.position, count); + + int writtenCount = 0; + if (fileDataRange.Intersection(rangeToRead) == null) + { + int readCountFromFooter; + if (TryReadFromFooter(rangeToRead, buffer, offset, out readCountFromFooter)) + { + writtenCount += readCountFromFooter; + } + return writtenCount; + } + + rangeToRead = fileDataRange.Intersection(rangeToRead); + + var startingBlock = ByteToBlock(rangeToRead.StartIndex); + var endingBlock = ByteToBlock(rangeToRead.EndIndex); + + for (var blockIndex = startingBlock; blockIndex <= endingBlock; blockIndex++) + { + var currentBlock = blockFactory.Create(blockIndex); + var rangeToReadInBlock = currentBlock.LogicalRange.Intersection(rangeToRead); + + var copyStartIndex = rangeToReadInBlock.StartIndex % blockFactory.GetBlockSize(); + Buffer.BlockCopy(currentBlock.Data, (int)copyStartIndex, buffer, offset + writtenCount, (int)rangeToReadInBlock.Length); + + writtenCount += (int)rangeToReadInBlock.Length; + } + this.position += writtenCount; + + return writtenCount; + } + catch (Exception e) + { + throw new VhdParsingException("Invalid or Corrupted VHD file", e); + } + } + + public bool TryReadFromFooter(IndexRange rangeToRead, byte[] buffer, int offset, out int readCount) + { + readCount = 0; + var rangeToReadFromFooter = this.footerRange.Intersection(rangeToRead); + if (rangeToReadFromFooter != null) + { + var footerData = GenerateFooter(); + var copyStartIndex = rangeToReadFromFooter.StartIndex - footerRange.StartIndex; + Buffer.BlockCopy(footerData, (int)copyStartIndex, buffer, offset, (int)rangeToReadFromFooter.Length); + this.position += (int)rangeToReadFromFooter.Length; + readCount = (int)rangeToReadFromFooter.Length; + return true; + } + return false; + } + + private uint ByteToBlock(long position) + { + uint sectorsPerBlock = (uint)(this.blockFactory.GetBlockSize() / VhdConstants.VHD_SECTOR_LENGTH); + return (uint)Math.Floor((position / VhdConstants.VHD_SECTOR_LENGTH) * 1.0m / sectorsPerBlock); + } + + private byte[] GenerateFooter() + { + var footer = vhdFile.Footer.CreateCopy(); + if (vhdFile.Footer.DiskType != DiskType.Fixed) + { + footer.HeaderOffset = VhdConstants.VHD_NO_DATA_LONG; + footer.DiskType = DiskType.Fixed; + footer.CreatorApplication = VhdFooterFactory.WindowsAzureCreatorApplicationName; + } + var serializer = new VhdFooterSerializer(footer); + return serializer.ToByteArray(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + this.Position = offset; + break; + case SeekOrigin.Current: + this.Position += offset; + break; + case SeekOrigin.End: + this.Position -= offset; + break; + default: + throw new NotSupportedException(); + } + return this.Position; + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + protected override void Dispose(bool disposing) + { + if (!isDisposed) + { + if (disposing) + { + this.vhdFile.Dispose(); + isDisposed = true; + } + } + } + } +} From 58ea9bbd238cd3bfa4c4ad995f9fb580f086370e Mon Sep 17 00:00:00 2001 From: MiYanni Date: Tue, 30 Oct 2018 16:58:15 -0700 Subject: [PATCH 10/15] Updated Stack #if statements. --- .../Extension/NetcoreExtension.cs | 79 ++++++++++--------- .../Cmdlets/Components/ApiVersionHelper.cs | 14 ++-- ...pertyNamesWithOverridesContractResolver.cs | 5 +- .../Models.ResourceGroups/ResourceClient.cs | 5 +- .../ResourcesBaseCmdlet.cs | 4 +- .../ResourcesExtensions.cs | 2 + 6 files changed, 59 insertions(+), 50 deletions(-) diff --git a/src/StackAdmin/Compute/Commands.Compute/Extension/NetcoreExtension.cs b/src/StackAdmin/Compute/Commands.Compute/Extension/NetcoreExtension.cs index 6362ca673b73..baf215076696 100644 --- a/src/StackAdmin/Compute/Commands.Compute/Extension/NetcoreExtension.cs +++ b/src/StackAdmin/Compute/Commands.Compute/Extension/NetcoreExtension.cs @@ -11,46 +11,9 @@ // See the License for the specific language governing permissions and // limitations under the License. // ---------------------------------------------------------------------------------- -#if !NETSTANDARD -using Microsoft.Azure.Management.Storage.Models; - -namespace Microsoft.Azure.Commands.Compute -{ - public static class StorageExtensions - { - public static SkuName? Sku(this StorageAccount account) - { - return account.Sku.Name; - } - - public static bool IsPremiumLrs(this StorageAccount account) - { - return account.Sku.Name== SkuName.PremiumLRS; - } - - public static void SetAsStandardGRS(this StorageAccountCreateParameters createParams) - { - createParams.Sku.Name = SkuName.StandardGRS; - } - - public static string GetFirstAvailableKey(this StorageAccountListKeysResult listKeyResult) - { - return !string.IsNullOrEmpty(listKeyResult.Keys[0].Value) ? listKeyResult.Keys[0].Value : listKeyResult.Keys[1].Value; - } - - public static string GetKey1(this StorageAccountListKeysResult listKeyResult) - { - return listKeyResult.Keys[0].Value; - } - - public static string GetKey2(this StorageAccountListKeysResult listKeyResult) - { - return listKeyResult.Keys[1].Value; - } - } -} -#else +// TODO: Remove IfDef +#if NETSTANDARD using Microsoft.Azure.Commands.Common.Authentication; using Microsoft.Azure.Commands.Common.Authentication.Abstractions; using Microsoft.Azure.Management.Network; @@ -783,4 +746,42 @@ public bool ShouldSerializeIpConfigurations() } #endregion +#else +using Microsoft.Azure.Management.Storage.Models; + +namespace Microsoft.Azure.Commands.Compute +{ + public static class StorageExtensions + { + public static SkuName? Sku(this StorageAccount account) + { + return account.Sku.Name; + } + + public static bool IsPremiumLrs(this StorageAccount account) + { + return account.Sku.Name== SkuName.PremiumLRS; + } + + public static void SetAsStandardGRS(this StorageAccountCreateParameters createParams) + { + createParams.Sku.Name = SkuName.StandardGRS; + } + + public static string GetFirstAvailableKey(this StorageAccountListKeysResult listKeyResult) + { + return !string.IsNullOrEmpty(listKeyResult.Keys[0].Value) ? listKeyResult.Keys[0].Value : listKeyResult.Keys[1].Value; + } + + public static string GetKey1(this StorageAccountListKeysResult listKeyResult) + { + return listKeyResult.Keys[0].Value; + } + + public static string GetKey2(this StorageAccountListKeysResult listKeyResult) + { + return listKeyResult.Keys[1].Value; + } + } +} #endif \ No newline at end of file diff --git a/src/StackAdmin/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs b/src/StackAdmin/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs index d2f3382799e7..781ea1ff2b7c 100644 --- a/src/StackAdmin/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs +++ b/src/StackAdmin/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs @@ -26,10 +26,11 @@ namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Components using System.Linq; using System.Threading; using System.Threading.Tasks; -#if !NETSTANDARD - using System.Runtime.Caching; -#else +// TODO: Remove IfDef +#if NETSTANDARD using Microsoft.Extensions.Caching.Memory; +#else + using System.Runtime.Caching; #endif /// @@ -145,10 +146,11 @@ private class ApiVersionCache static ApiVersionCache() { -#if !NETSTANDARD - _cache = MemoryCache.Default; -#else +// TODO: Remove IfDef +#if NETSTANDARD _cache = new MemoryCache(new MemoryCacheOptions()); +#else + _cache = MemoryCache.Default; #endif } /// diff --git a/src/StackAdmin/Resources/Commands.ResourceManager/Cmdlets/Json/CamelCasePropertyNamesWithOverridesContractResolver.cs b/src/StackAdmin/Resources/Commands.ResourceManager/Cmdlets/Json/CamelCasePropertyNamesWithOverridesContractResolver.cs index 84c814d1bb27..aa87192fb6fe 100644 --- a/src/StackAdmin/Resources/Commands.ResourceManager/Cmdlets/Json/CamelCasePropertyNamesWithOverridesContractResolver.cs +++ b/src/StackAdmin/Resources/Commands.ResourceManager/Cmdlets/Json/CamelCasePropertyNamesWithOverridesContractResolver.cs @@ -55,13 +55,14 @@ protected override JsonDictionaryContract CreateDictionaryContract(Type objectTy { var contract = base.CreateDictionaryContract(objectType); +// TODO: Remove IfDef code +#if !NETSTANDARD var attributes = objectType.GetCustomAttributes(attributeType: typeof(JsonPreserveCaseDictionaryAttribute), inherit: true); if (attributes.Any()) { -#if !NETSTANDARD contract.PropertyNameResolver = propertyName => propertyName; -#endif } +#endif return contract; } diff --git a/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourceClient.cs b/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourceClient.cs index 3b1abca56567..a266b8e9c3e4 100644 --- a/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourceClient.cs +++ b/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourceClient.cs @@ -53,7 +53,7 @@ public partial class ResourcesClient public IResourceManagementClient ResourceManagementClient { get; set; } public IAuthorizationManagementClient AuthorizationManagementClient { get; set; } - +// TODO: Remove IfDef code #if !NETSTANDARD public GalleryTemplatesClient GalleryTemplatesClient { get; set; } #endif @@ -71,6 +71,7 @@ public partial class ResourcesClient public ResourcesClient(IAzureContext context) : this( AzureSession.Instance.ClientFactory.CreateArmClient(context, AzureEnvironment.Endpoint.ResourceManager), +// TODO: Remove IfDef code #if !NETSTANDARD new GalleryTemplatesClient(context), #endif @@ -87,11 +88,13 @@ public ResourcesClient(IAzureContext context) /// The management client instance public ResourcesClient( IResourceManagementClient resourceManagementClient, +// TODO: Remove IfDef code #if !NETSTANDARD GalleryTemplatesClient galleryTemplatesClient, #endif IAuthorizationManagementClient authorizationManagementClient) { +// TODO: Remove IfDef code #if !NETSTANDARD GalleryTemplatesClient = galleryTemplatesClient; #endif diff --git a/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourcesBaseCmdlet.cs b/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourcesBaseCmdlet.cs index 7fbfeaae4ad3..eec98cfb6706 100644 --- a/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourcesBaseCmdlet.cs +++ b/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourcesBaseCmdlet.cs @@ -27,7 +27,7 @@ public abstract class ResourcesBaseCmdlet : AzureRMCmdlet /// Field that holds the resource client instance /// private ResourcesClient resourcesClient; - +// TODO: Remove IfDef code #if !NETSTANDARD /// /// Field that holds the gallery templates client instance @@ -66,7 +66,7 @@ public ResourcesClient ResourcesClient set { this.resourcesClient = value; } } - +// TODO: Remove IfDef code #if !NETSTANDARD /// /// Gets or sets the gallery templates client diff --git a/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs b/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs index 41d8365856fa..48501b896178 100644 --- a/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs +++ b/src/StackAdmin/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs @@ -27,6 +27,7 @@ using Microsoft.WindowsAzure.Commands.Common; using Microsoft.Azure.Management.ResourceManager.Models; using Microsoft.Azure.Commands.Resources.Models; +// TODO: Remove IfDef code #if !NETSTANDARD using Microsoft.Azure.Commands.Resources.Models.Gallery; #endif @@ -35,6 +36,7 @@ namespace Microsoft.Azure.Commands.Resources.Models { public static class ResourcesExtensions { +// TODO: Remove IfDef code #if !NETSTANDARD public static PSGalleryItem ToPSGalleryItem(this GalleryItem gallery) { From e43ceb4696549a84de082fa045ffcb7d00768baa Mon Sep 17 00:00:00 2001 From: MiYanni Date: Tue, 30 Oct 2018 17:23:43 -0700 Subject: [PATCH 11/15] Updated Storage #if statements. --- .../Common/CmdletOperationContext.cs | 77 ++++---- .../Common/StorageCloudCmdletBase.cs | 184 ++++++++---------- 2 files changed, 114 insertions(+), 147 deletions(-) diff --git a/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs b/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs index b9f29e2a774f..48dac7f01842 100644 --- a/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs +++ b/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs @@ -20,8 +20,8 @@ namespace Microsoft.WindowsAzure.Commands.Storage.Common internal class CmdletOperationContext { - private static volatile bool inited; - private static object syncRoot = new Object(); + private static volatile bool _inited; + private static readonly object SyncRoot = new Object(); public static DateTime StartTime { @@ -38,26 +38,26 @@ public static string ClientRequestId /// /// started remote call counter /// - private static int startedRemoteCallCounter = 0; + private static int _startedRemoteCallCounter; public static int StartedRemoteCallCounter { get { - return startedRemoteCallCounter; + return _startedRemoteCallCounter; } } /// /// finished remote call counter /// - private static int finishedRemoteCallCounter = 0; + private static int _finishedRemoteCallCounter; public static int FinishedRemoteCallCounter { get { - return finishedRemoteCallCounter; + return _finishedRemoteCallCounter; } } @@ -68,17 +68,15 @@ private CmdletOperationContext() { } /// public static void Init() { - if (!inited) + if (_inited) return; + + lock (SyncRoot) { - lock (syncRoot) - { - if (!inited) - { - StartTime = DateTime.Now; - ClientRequestId = GenClientRequestID(); - inited = true; - } - } + if (_inited) return; + + StartTime = DateTime.Now; + ClientRequestId = GenClientRequestID(); + _inited = true; } } @@ -88,34 +86,34 @@ public static void Init() /// A unique request id internal static string GenClientRequestID() { - string uniqueId = System.Guid.NewGuid().ToString(); + var uniqueId = System.Guid.NewGuid().ToString(); return string.Format(Resources.ClientRequestIdFormat, uniqueId); } /// /// Get Storage Operation Context for rest calls /// - /// Ouput writer for writing logs for each rest call + /// Output writer for writing logs for each rest call /// Storage operation context public static OperationContext GetStorageOperationContext(Action outputWriter) { - if (!inited) + if (!_inited) { - CmdletOperationContext.Init(); + Init(); } - OperationContext context = new OperationContext(); - context.ClientRequestID = ClientRequestId; + var context = new OperationContext {ClientRequestID = ClientRequestId}; context.SendingRequest += (s, e) => { context.StartTime = DateTime.Now; - Interlocked.Increment(ref startedRemoteCallCounter); + Interlocked.Increment(ref _startedRemoteCallCounter); +// TODO: Remove IfDef #if NETSTANDARD //https://github.com/Azure/azure-storage-net/issues/658 - string message = String.Format(Resources.StartRemoteCall, - startedRemoteCallCounter, String.Empty, e.RequestUri.ToString()); + var message = String.Format(Resources.StartRemoteCall, + _startedRemoteCallCounter, String.Empty, e.RequestUri.ToString()); #else string message = String.Format(Resources.StartRemoteCall, startedRemoteCallCounter, e.Request.Method, e.Request.RequestUri.ToString()); @@ -123,10 +121,7 @@ public static OperationContext GetStorageOperationContext(Action outputW try { - if (outputWriter != null) - { - outputWriter(message); - } + outputWriter?.Invoke(message); } catch { @@ -137,12 +132,13 @@ public static OperationContext GetStorageOperationContext(Action outputW context.ResponseReceived += (s, e) => { context.EndTime = DateTime.Now; - Interlocked.Increment(ref finishedRemoteCallCounter); + Interlocked.Increment(ref _finishedRemoteCallCounter); - double elapsedTime = (context.EndTime - context.StartTime).TotalMilliseconds; + var elapsedTime = (context.EndTime - context.StartTime).TotalMilliseconds; +// TODO: Remove IfDef #if NETSTANDARD //https://github.com/Azure/azure-storage-net/issues/658 - string message = String.Format(Resources.FinishRemoteCall, + var message = String.Format(Resources.FinishRemoteCall, e.RequestUri.ToString(), String.Empty, String.Empty, e.RequestInformation.ServiceRequestID, elapsedTime); #else string message = String.Format(Resources.FinishRemoteCall, @@ -151,10 +147,7 @@ public static OperationContext GetStorageOperationContext(Action outputW try { - if (outputWriter != null) - { - outputWriter(message); - } + outputWriter?.Invoke(message); } catch { @@ -166,20 +159,18 @@ public static OperationContext GetStorageOperationContext(Action outputW } /// - /// Get the running ms from when operationcontext started + /// Get the running ms from when operation context started /// /// A time string in ms public static double GetRunningMilliseconds() { - if (!inited) + if (!_inited) { return 0; } - else - { - TimeSpan span = DateTime.Now - StartTime; - return span.TotalMilliseconds; - } + + var span = DateTime.Now - StartTime; + return span.TotalMilliseconds; } } } diff --git a/src/Storage/Commands.Storage/Common/StorageCloudCmdletBase.cs b/src/Storage/Commands.Storage/Common/StorageCloudCmdletBase.cs index 805771484a3c..699e5dcb88bc 100644 --- a/src/Storage/Commands.Storage/Common/StorageCloudCmdletBase.cs +++ b/src/Storage/Commands.Storage/Common/StorageCloudCmdletBase.cs @@ -57,24 +57,12 @@ public class StorageCloudCmdletBase : AzureDataCmdlet /// [Parameter(Mandatory = false, HelpMessage = "The credentials, account, tenant, and subscription used for communication with Azure.")] [Alias("AzureRmContext", "AzureCredential")] - public IAzureContextContainer DefaultProfile - { - get - { - return _profile; - } - set - { - _profile = value; - } - } - - private IAzureContextContainer _profile; + public IAzureContextContainer DefaultProfile { get; set; } /// /// Amount of concurrent async tasks to run per available core. /// - protected int concurrentTaskCount = 10; + private int _concurrentTaskCount = 10; /// /// Amount of concurrent async tasks to run per available core. @@ -84,14 +72,14 @@ public IAzureContextContainer DefaultProfile [ValidateRange(1, 1000)] public virtual int? ConcurrentTaskCount { - get { return concurrentTaskCount; } + get { return _concurrentTaskCount; } set { - int count = value.Value; + var count = value.Value; if (count > 0) { - concurrentTaskCount = count; + _concurrentTaskCount = count; } } } @@ -148,13 +136,13 @@ protected virtual T CreateChannel() /// /// Cancellation Token Source /// - private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); protected CancellationToken CmdletCancellationToken; /// /// whether stop processing /// - protected bool ShouldForceQuit { get { return cancellationTokenSource.Token.IsCancellationRequested; } } + protected bool ShouldForceQuit { get { return _cancellationTokenSource.Token.IsCancellationRequested; } } /// /// Enable or disable multithread @@ -163,10 +151,10 @@ protected virtual T CreateChannel() /// protected bool EnableMultiThread { - get { return enableMultiThread; } - set { enableMultiThread = value; } + get { return _enableMultiThread; } + set { _enableMultiThread = value; } } - private bool enableMultiThread = true; + private bool _enableMultiThread = true; internal TaskOutputStream OutputStream; @@ -178,7 +166,7 @@ protected bool EnableMultiThread /// protected ProgressRecord summaryRecord; - private LimitedConcurrencyTaskScheduler taskScheduler; + private LimitedConcurrencyTaskScheduler _taskScheduler; /// /// Cmdlet operation context. @@ -227,14 +215,14 @@ public IRequestOptions GetRequestOptions(StorageServiceType type) throw new ArgumentException(Resources.InvalidStorageServiceType, "type"); } - if (this.ServerTimeoutPerRequest.HasValue) + if (ServerTimeoutPerRequest.HasValue) { - options.ServerTimeout = ConvertToTimeSpan(this.ServerTimeoutPerRequest.Value); + options.ServerTimeout = ConvertToTimeSpan(ServerTimeoutPerRequest.Value); } - if (this.ClientTimeoutPerRequest.HasValue) + if (ClientTimeoutPerRequest.HasValue) { - options.MaximumExecutionTime = ConvertToTimeSpan(this.ClientTimeoutPerRequest.Value); + options.MaximumExecutionTime = ConvertToTimeSpan(ClientTimeoutPerRequest.Value); } return options; @@ -246,14 +234,14 @@ public IRequestOptions GetRequestOptions(StorageServiceType type) /// Storage account internal AzureStorageContext GetCmdletStorageContext() { - var context = this.GetCmdletStorageContext(this.Context); - this.Context = context; + var context = GetCmdletStorageContext(Context); + Context = context; return context; } internal AzureStorageContext GetCmdletStorageContext(IStorageContext inContext) { - AzureStorageContext context = inContext as AzureStorageContext; + var context = inContext as AzureStorageContext; if (context == null && inContext != null) { context = new AzureStorageContext(inContext.GetCloudStorageAccount()); @@ -322,13 +310,11 @@ protected virtual void InitChannelCurrentSubscription(bool force) internal virtual bool TryGetStorageAccount(IAzureContextContainer profile, out string account) { account = null; - bool result = false; //Storage Context is empty and have already set the current storage account in subscription - if (Context == null && profile != null && profile.DefaultContext != null && profile.DefaultContext.Subscription != null) - { - account = profile.DefaultContext.GetCurrentStorageAccountConnectionString(); - result = !string.IsNullOrWhiteSpace(account); - } + if (Context != null || profile?.DefaultContext?.Subscription == null) return false; + + account = profile.DefaultContext.GetCurrentStorageAccountConnectionString(); + var result = !string.IsNullOrWhiteSpace(account); return result; } @@ -344,7 +330,7 @@ internal void WriteObjectWithStorageContext(IEnumerable itemLi return; } - foreach (AzureStorageBase item in itemList) + foreach (var item in itemList) { WriteObjectWithStorageContext(item); } @@ -367,19 +353,16 @@ internal void WriteObjectWithStorageContext(IEnumerable itemLi { return null; } - else - { - return timeSpan; - } + + return timeSpan; } - else if (timeoutInSeconds == Timeout.Infinite) + + if (timeoutInSeconds == Timeout.Infinite) { return null; } - else - { - throw new ArgumentOutOfRangeException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidTimeoutValue, timeoutInSeconds)); - } + + throw new ArgumentOutOfRangeException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidTimeoutValue, timeoutInSeconds)); } @@ -387,16 +370,11 @@ internal void WriteObjectWithStorageContext(IEnumerable itemLi /// Get storage account from a connection string /// /// Cloud storage account - private bool TryGetStorageAccountFromEnvironmentVariable(out string connectionString) + private static bool TryGetStorageAccountFromEnvironmentVariable(out string connectionString) { - connectionString = System.Environment.GetEnvironmentVariable(Resources.EnvConnectionString); + connectionString = Environment.GetEnvironmentVariable(Resources.EnvConnectionString); - if (String.IsNullOrEmpty(connectionString)) - { - return false; - } - - return true; + return !String.IsNullOrEmpty(connectionString); } private CloudStorageAccount GetStorageAccountFromConnectionString(string connectionString) @@ -405,19 +383,17 @@ private CloudStorageAccount GetStorageAccountFromConnectionString(string connect { throw new ArgumentException(Resources.DefaultStorageCredentialsNotFound); } - else - { - WriteDebugLog(Resources.GetStorageAccountFromEnvironmentVariable); - try - { - return CloudStorageAccount.Parse(connectionString); - } - catch - { - WriteVerboseWithTimestamp(Resources.CannotGetStorageAccountFromEnvironmentVariable); - throw; - } + WriteDebugLog(Resources.GetStorageAccountFromEnvironmentVariable); + + try + { + return CloudStorageAccount.Parse(connectionString); + } + catch + { + WriteVerboseWithTimestamp(Resources.CannotGetStorageAccountFromEnvironmentVariable); + throw; } } @@ -443,13 +419,13 @@ protected override void WriteExceptionError(Exception e) } /// - /// Get the error category for specificed exception + /// Get the error category for specified exception /// /// Exception object /// Error category protected ErrorCategory GetExceptionErrorCategory(Exception e) { - ErrorCategory errorCategory = ErrorCategory.CloseError; //default error category + var errorCategory = ErrorCategory.CloseError; //default error category if (e is ArgumentException) { @@ -483,7 +459,7 @@ protected void WriteTerminatingError(Exception e) /// The max number of concurrent task/rest call protected int GetCmdletConcurrency() { - return concurrentTaskCount; + return _concurrentTaskCount; } /// @@ -491,8 +467,8 @@ protected int GetCmdletConcurrency() /// private void ConfigureServicePointManager() { - int maxConcurrency = 1000; - int cmdletConcurrency = GetCmdletConcurrency(); + var maxConcurrency = 1000; + var cmdletConcurrency = GetCmdletConcurrency(); maxConcurrency = Math.Max(maxConcurrency, cmdletConcurrency); //Set the default connection limit to a very high value and control the concurrency with LimitedConcurrencyTaskScheduler. //If so, there is no need to set the ConnectionLimit for each ServicePoint. @@ -503,10 +479,7 @@ private void ConfigureServicePointManager() private void TaskErrorHandler(object sender, TaskExceptionEventArgs args) { - if (OutputStream != null) - { - OutputStream.WriteError(args.TaskId, args.Exception); - } + OutputStream?.WriteError(args.TaskId, args.Exception); } /// @@ -514,21 +487,23 @@ private void TaskErrorHandler(object sender, TaskExceptionEventArgs args) /// internal void InitMutltiThreadResources() { - taskScheduler = new LimitedConcurrencyTaskScheduler(GetCmdletConcurrency(), CmdletCancellationToken); - OutputStream = new TaskOutputStream(CmdletCancellationToken); - OutputStream.OutputWriter = WriteObject; - OutputStream.ErrorWriter = WriteExceptionError; - OutputStream.ProgressWriter = WriteProgress; - OutputStream.VerboseWriter = WriteVerbose; - OutputStream.DebugWriter = WriteDebugWithTimestamp; - OutputStream.ConfirmWriter = ShouldProcess; - OutputStream.TaskStatusQueryer = taskScheduler.IsTaskCompleted; - taskScheduler.OnError += TaskErrorHandler; - - int summaryRecordId = 0; - string summary = String.Format(Resources.TransmitActiveSummary, taskScheduler.TotalTaskCount, - taskScheduler.FinishedTaskCount, taskScheduler.FailedTaskCount, taskScheduler.ActiveTaskCount); - string activity = string.Format(Resources.TransmitActivity, this.MyInvocation.MyCommand); + _taskScheduler = new LimitedConcurrencyTaskScheduler(GetCmdletConcurrency(), CmdletCancellationToken); + OutputStream = new TaskOutputStream(CmdletCancellationToken) + { + OutputWriter = WriteObject, + ErrorWriter = WriteExceptionError, + ProgressWriter = WriteProgress, + VerboseWriter = WriteVerbose, + DebugWriter = WriteDebugWithTimestamp, + ConfirmWriter = ShouldProcess, + TaskStatusQueryer = _taskScheduler.IsTaskCompleted + }; + _taskScheduler.OnError += TaskErrorHandler; + + const int summaryRecordId = 0; + var summary = String.Format(Resources.TransmitActiveSummary, _taskScheduler.TotalTaskCount, + _taskScheduler.FinishedTaskCount, _taskScheduler.FailedTaskCount, _taskScheduler.ActiveTaskCount); + var activity = string.Format(Resources.TransmitActivity, MyInvocation.MyCommand); summaryRecord = new ProgressRecord(summaryRecordId, activity, summary); CmdletCancellationToken.Register(() => OutputStream.CancelConfirmRequest()); } @@ -554,7 +529,7 @@ internal void MultiThreadEndProcessing() //So, we'd better output status at first. OutputStream.Output(); } - while (!taskScheduler.WaitForComplete(WaitTimeout, CmdletCancellationToken)); + while (!_taskScheduler.WaitForComplete(WaitTimeout, CmdletCancellationToken)); CloseSummaryProgressBar(); OutputStream.Output(); @@ -562,8 +537,8 @@ internal void MultiThreadEndProcessing() protected void WriteTaskSummary() { - WriteVerbose(String.Format(Resources.TransferSummary, taskScheduler.TotalTaskCount, - taskScheduler.FinishedTaskCount, taskScheduler.FailedTaskCount, taskScheduler.ActiveTaskCount)); + WriteVerbose(String.Format(Resources.TransferSummary, _taskScheduler.TotalTaskCount, + _taskScheduler.FinishedTaskCount, _taskScheduler.FailedTaskCount, _taskScheduler.ActiveTaskCount)); } /// @@ -578,7 +553,7 @@ private void CloseSummaryProgressBar() internal void RunTask(Func taskGenerator) { - taskScheduler.RunTask(taskGenerator); + _taskScheduler.RunTask(taskGenerator); } /// @@ -586,8 +561,8 @@ internal void RunTask(Func taskGenerator) /// protected virtual void WriteTransmitSummaryStatus() { - string summary = String.Format(Resources.TransmitActiveSummary, taskScheduler.TotalTaskCount, - taskScheduler.FinishedTaskCount, taskScheduler.FailedTaskCount, taskScheduler.ActiveTaskCount); + var summary = String.Format(Resources.TransmitActiveSummary, _taskScheduler.TotalTaskCount, + _taskScheduler.FinishedTaskCount, _taskScheduler.FailedTaskCount, _taskScheduler.ActiveTaskCount); summaryRecord.StatusDescription = summary; WriteProgress(summaryRecord); } @@ -598,10 +573,10 @@ protected virtual void WriteTransmitSummaryStatus() protected override void BeginProcessing() { CmdletOperationContext.Init(); - CmdletCancellationToken = cancellationTokenSource.Token; - WriteDebugLog(String.Format(Resources.InitOperationContextLog, this.GetType().Name, CmdletOperationContext.ClientRequestId)); + CmdletCancellationToken = _cancellationTokenSource.Token; + WriteDebugLog(String.Format(Resources.InitOperationContextLog, GetType().Name, CmdletOperationContext.ClientRequestId)); - if (enableMultiThread) + if (_enableMultiThread) { SetUpMultiThreadEnvironment(); } @@ -610,6 +585,7 @@ protected override void BeginProcessing() (sender, args) => { //https://github.com/Azure/azure-storage-net/issues/658 +// TODO: Remove IfDef code #if !NETSTANDARD args.Request.UserAgent = Microsoft.WindowsAzure.Storage.Shared.Protocol.Constants.HeaderConstants.UserAgent + " " + ApiConstants.UserAgentHeaderValue; #endif @@ -623,14 +599,14 @@ protected override void BeginProcessing() /// protected override void EndProcessing() { - if (enableMultiThread) + if (_enableMultiThread) { MultiThreadEndProcessing(); } - double timespan = CmdletOperationContext.GetRunningMilliseconds(); - string message = string.Format(Resources.EndProcessingLog, - this.GetType().Name, CmdletOperationContext.StartedRemoteCallCounter, CmdletOperationContext.FinishedRemoteCallCounter, timespan, CmdletOperationContext.ClientRequestId); + var timespan = CmdletOperationContext.GetRunningMilliseconds(); + var message = string.Format(Resources.EndProcessingLog, + GetType().Name, CmdletOperationContext.StartedRemoteCallCounter, CmdletOperationContext.FinishedRemoteCallCounter, timespan, CmdletOperationContext.ClientRequestId); WriteDebugLog(message); base.EndProcessing(); } @@ -642,7 +618,7 @@ protected override void EndProcessing() protected override void StopProcessing() { //ctrl + c and etc - cancellationTokenSource.Cancel(); + _cancellationTokenSource.Cancel(); base.StopProcessing(); } From 12c35048ed3a6e9d5068641afc945bf830a81a76 Mon Sep 17 00:00:00 2001 From: MiYanni Date: Tue, 30 Oct 2018 18:50:22 -0700 Subject: [PATCH 12/15] Updated StaticAnalysis and Tools.Common #if statements. --- .../BreakingChangeAnalyzer.cs | 155 ++++---- .../BreakingChangeAttributesAnalyzer.cs | 111 +++--- .../CmdletBreakingChangeAttributeLoader.cs | 25 +- .../DependencyAnalyzer/DependencyAnalyzer.cs | 171 ++++---- .../HelpAnalyzer/HelpAnalyzer.cs | 181 +++++---- .../SignatureVerifier/SignatureVerifier.cs | 375 +++++++++--------- .../Helpers/EnvironmentHelpers.cs | 3 +- tools/Tools.Common/Loaders/AssemblyLoader.cs | 17 +- tools/Tools.Common/Loaders/CmdletLoader.cs | 31 +- tools/Tools.Common/Loggers/AnalysisLogger.cs | 48 +-- 10 files changed, 547 insertions(+), 570 deletions(-) diff --git a/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs b/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs index b7a8c4484aa4..48c47f550186 100644 --- a/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs +++ b/tools/StaticAnalysis/BreakingChangeAnalyzer/BreakingChangeAnalyzer.cs @@ -37,6 +37,7 @@ public class BreakingChangeAnalyzer : IStaticAnalyzer public string Name { get; set; } public string BreakingChangeIssueReportLoggerName { get; set; } +// TODO: Remove IfDef code #if !NETSTANDARD private AppDomain _appDomain; #endif @@ -85,7 +86,6 @@ public void Analyze( Func cmdletFilter, IEnumerable modulesToAnalyze) { - var savedDirectory = Directory.GetCurrentDirectory(); var processedHelpFiles = new List(); var issueLogger = Logger.CreateLogger("BreakingChangeIssues.csv"); @@ -97,17 +97,16 @@ public void Analyze( foreach (var baseDirectory in cmdletProbingDirs.Where(s => !s.Contains("ServiceManagement") && !s.Contains("Stack") && Directory.Exists(Path.GetFullPath(s)))) { - List probingDirectories = new List(); + var probingDirectories = new List {baseDirectory}; // Add current directory for probing - probingDirectories.Add(baseDirectory); probingDirectories.AddRange(Directory.EnumerateDirectories(Path.GetFullPath(baseDirectory))); foreach (var directory in probingDirectories) { if (modulesToAnalyze != null && modulesToAnalyze.Any() && - !modulesToAnalyze.Where(m => directory.EndsWith(m)).Any()) + !modulesToAnalyze.Any(m => directory.EndsWith(m))) { continue; } @@ -129,99 +128,86 @@ public void Analyze( var psd1 = manifestFiles.FirstOrDefault(); var parentDirectory = Directory.GetParent(psd1).FullName; var psd1FileName = Path.GetFileName(psd1); - IEnumerable nestedModules = null; - List requiredModules = null; - PowerShell powershell = PowerShell.Create(); + var powershell = PowerShell.Create(); powershell.AddScript("Import-LocalizedData -BaseDirectory " + parentDirectory + " -FileName " + psd1FileName + " -BindingVariable ModuleMetadata; $ModuleMetadata.NestedModules; $ModuleMetadata.RequiredModules | % { $_[\"ModuleName\"] };"); var cmdletResult = powershell.Invoke(); - nestedModules = cmdletResult.Where(c => c.ToString().StartsWith(".")).Select(c => c.ToString().Substring(2)); - requiredModules = cmdletResult.Where(c => !c.ToString().StartsWith(".")).Select(c => c.ToString()).ToList(); + var nestedModules = cmdletResult.Where(c => c.ToString().StartsWith(".")).Select(c => c.ToString().Substring(2)); + var requiredModules = cmdletResult.Where(c => !c.ToString().StartsWith(".")).Select(c => c.ToString()).ToList(); - if (nestedModules.Any()) - { - Directory.SetCurrentDirectory(directory); + if (!nestedModules.Any()) continue; - requiredModules = requiredModules.Join(cmdletProbingDirs, - module => 1, - dir => 1, - (module, dir) => Path.Combine(dir, module)) - .Where(f => Directory.Exists(f)) - .ToList(); + Directory.SetCurrentDirectory(directory); - requiredModules.Add(directory); + requiredModules = requiredModules.Join(cmdletProbingDirs, + module => 1, + dir => 1, + (module, dir) => Path.Combine(dir, module)) + .Where(Directory.Exists) + .ToList(); - foreach (var nestedModule in nestedModules) - { - var assemblyFile = Directory.GetFiles(parentDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault(); - var assemblyFileName = Path.GetFileName(assemblyFile); - if (File.Exists(assemblyFile)) - { - issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = assemblyFileName, "AssemblyFileName"); - processedHelpFiles.Add(assemblyFileName); - var proxy = -#if !NETSTANDARD - EnvironmentHelpers.CreateProxy(directory, out _appDomain); + requiredModules.Add(directory); + + foreach (var nestedModule in nestedModules) + { + var assemblyFile = Directory.GetFiles(parentDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault(); + var assemblyFileName = Path.GetFileName(assemblyFile); + if (!File.Exists(assemblyFile)) continue; + + issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = assemblyFileName, "AssemblyFileName"); + processedHelpFiles.Add(assemblyFileName); +// TODO: Remove IfDef +#if NETSTANDARD + var proxy = new CmdletLoader(); #else - new CmdletLoader(); + var proxy = EnvironmentHelpers.CreateProxy(directory, out _appDomain); #endif - var newModuleMetadata = proxy.GetModuleMetadata(assemblyFile, requiredModules); + var newModuleMetadata = proxy.GetModuleMetadata(assemblyFile, requiredModules); - string fileName = assemblyFileName + ".json"; - string executingPath = - Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); + var fileName = assemblyFileName + ".json"; + var executingPath = + Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath); - string filePath = executingPath + "\\SerializedCmdlets\\" + fileName; - bool serialize = false; - - if (serialize) - { - SerializeCmdlets(filePath, newModuleMetadata); - } - else - { - if (!File.Exists(filePath)) - { - continue; - } - - var oldModuleMetadata = DeserializeCmdlets(filePath); + var filePath = executingPath + "\\SerializedCmdlets\\" + fileName; + if (!File.Exists(filePath)) + { + continue; + } - if (cmdletFilter != null) - { - string output = string.Format("Before filter\nOld module cmdlet count: {0}\nNew module cmdlet count: {1}", - oldModuleMetadata.Cmdlets.Count, newModuleMetadata.Cmdlets.Count); + var oldModuleMetadata = DeserializeCmdlets(filePath); - output += string.Format("\nCmdlet file: {0}", assemblyFileName); + if (cmdletFilter != null) + { + var output = string.Format("Before filter\nOld module cmdlet count: {0}\nNew module cmdlet count: {1}", + oldModuleMetadata.Cmdlets.Count, newModuleMetadata.Cmdlets.Count); - oldModuleMetadata.FilterCmdlets(cmdletFilter); - newModuleMetadata.FilterCmdlets(cmdletFilter); + output += string.Format("\nCmdlet file: {0}", assemblyFileName); - output += string.Format("After filter\nOld module cmdlet count: {0}\nNew module cmdlet count: {1}", - oldModuleMetadata.Cmdlets.Count, newModuleMetadata.Cmdlets.Count); + oldModuleMetadata.FilterCmdlets(cmdletFilter); + newModuleMetadata.FilterCmdlets(cmdletFilter); - foreach (var cmdlet in oldModuleMetadata.Cmdlets) - { - output += string.Format("\n\tOld cmdlet - {0}", cmdlet.Name); - } + output += string.Format("After filter\nOld module cmdlet count: {0}\nNew module cmdlet count: {1}", + oldModuleMetadata.Cmdlets.Count, newModuleMetadata.Cmdlets.Count); - foreach (var cmdlet in newModuleMetadata.Cmdlets) - { - output += string.Format("\n\tNew cmdlet - {0}", cmdlet.Name); - } + foreach (var cmdlet in oldModuleMetadata.Cmdlets) + { + output += string.Format("\n\tOld cmdlet - {0}", cmdlet.Name); + } - issueLogger.WriteMessage(output + Environment.NewLine); - } + foreach (var cmdlet in newModuleMetadata.Cmdlets) + { + output += string.Format("\n\tNew cmdlet - {0}", cmdlet.Name); + } - RunBreakingChangeChecks(oldModuleMetadata, newModuleMetadata, issueLogger); - } + issueLogger.WriteMessage(output + Environment.NewLine); + } + RunBreakingChangeChecks(oldModuleMetadata, newModuleMetadata, issueLogger); +// TODO: Remove IfDef code #if !NETSTANDARD - AppDomain.Unload(_appDomain); + AppDomain.Unload(_appDomain); #endif - } - } } } } @@ -234,7 +220,7 @@ public void Analyze( /// List of cmdlets that are to be serialized. private void SerializeCmdlets(string fileName, ModuleMetadata moduleMetadata) { - string json = JsonConvert.SerializeObject(moduleMetadata, Formatting.Indented); + var json = JsonConvert.SerializeObject(moduleMetadata, Formatting.Indented); File.WriteAllText(fileName, json); } @@ -243,7 +229,7 @@ private void SerializeCmdlets(string fileName, ModuleMetadata moduleMetadata) /// /// Name of the file we are to deserialize the cmdlets from. /// - private ModuleMetadata DeserializeCmdlets(string fileName) + private static ModuleMetadata DeserializeCmdlets(string fileName) { return JsonConvert.DeserializeObject(File.ReadAllText(fileName)); } @@ -268,10 +254,10 @@ private void RunBreakingChangeChecks( var newTypeDictionary = newModuleMetadata.TypeDictionary; // Initialize a TypeMetadataHelper object that knows how to compare types - TypeMetadataHelper typeMetadataHelper = new TypeMetadataHelper(oldTypeDictionary, newTypeDictionary); + var typeMetadataHelper = new TypeMetadataHelper(oldTypeDictionary, newTypeDictionary); // Initialize a CmdletMetadataHelper object that knows how to compare cmdlets - CmdletMetadataHelper cmdletMetadataHelper = new CmdletMetadataHelper(typeMetadataHelper); + var cmdletMetadataHelper = new CmdletMetadataHelper(typeMetadataHelper); // Compare the cmdlet metadata cmdletMetadataHelper.CompareCmdletMetadata(oldCmdlets, newCmdlets, issueLogger); @@ -279,14 +265,13 @@ private void RunBreakingChangeChecks( public AnalysisReport GetAnalysisReport() { - AnalysisReport analysisReport = new AnalysisReport(); - ReportLogger reportLog = Logger.GetReportLogger(BreakingChangeIssueReportLoggerName); - if (reportLog.Records.Any()) + var analysisReport = new AnalysisReport(); + var reportLog = Logger.GetReportLogger(BreakingChangeIssueReportLoggerName); + if (!reportLog.Records.Any()) return analysisReport; + + foreach (var rec in reportLog.Records) { - foreach (IReportRecord rec in reportLog.Records) - { - analysisReport.ProblemIdList.Add(rec.ProblemId); - } + analysisReport.ProblemIdList.Add(rec.ProblemId); } return analysisReport; diff --git a/tools/StaticAnalysis/BreakingChangeAttributesAnalyzer/BreakingChangeAttributesAnalyzer.cs b/tools/StaticAnalysis/BreakingChangeAttributesAnalyzer/BreakingChangeAttributesAnalyzer.cs index dac9c50a662e..531f8de928e7 100644 --- a/tools/StaticAnalysis/BreakingChangeAttributesAnalyzer/BreakingChangeAttributesAnalyzer.cs +++ b/tools/StaticAnalysis/BreakingChangeAttributesAnalyzer/BreakingChangeAttributesAnalyzer.cs @@ -12,6 +12,7 @@ // limitations under the License. // ---------------------------------------------------------------------------------- +// TODO: Remove IfDef code #if !NETSTANDARD using Microsoft.WindowsAzure.Commands.Common.CustomAttributes; #endif @@ -30,14 +31,14 @@ namespace StaticAnalysis.BreakingChangeAttributesAnalyzer { - class BreakingChangeAttributesAnalyzer : IStaticAnalyzer + internal class BreakingChangeAttributesAnalyzer : IStaticAnalyzer { public AnalysisLogger Logger { get; set; } public string Name { get; set; } public bool CleanBreakingChangesFileBeforeWriting { get; set; } public string OutputFilePath { get; set; } public string BreakingChangeAttributeReportLoggerName { get; protected set; } - +// TODO: Remove IfDef code #if !NETSTANDARD private AppDomain _appDomain; #endif @@ -91,8 +92,6 @@ public void Analyze(IEnumerable cmdletProbingDirs, FuncThe set of modules to analyze public void Analyze(IEnumerable cmdletProbingDirs, Func, IEnumerable> directoryFilter, Func cmdletFilter, IEnumerable modulesToAnalyze) { - var savedDirectory = Directory.GetCurrentDirectory(); - if (directoryFilter != null) { cmdletProbingDirs = directoryFilter(cmdletProbingDirs); @@ -100,17 +99,16 @@ public void Analyze(IEnumerable cmdletProbingDirs, Func !s.Contains("ServiceManagement") && !s.Contains("Stack") && Directory.Exists(Path.GetFullPath(s)))) { - List probingDirectories = new List(); + var probingDirectories = new List {baseDirectory}; // Add current directory for probing - probingDirectories.Add(baseDirectory); probingDirectories.AddRange(Directory.EnumerateDirectories(Path.GetFullPath(baseDirectory))); @@ -118,61 +116,60 @@ public void Analyze(IEnumerable cmdletProbingDirs, Func directory.EndsWith(m)).Any()) + !modulesToAnalyze.Any(m => directory.EndsWith(m))) { continue; } - IEnumerable cmdlets = GetCmdletsFilesInFolder(directory); - if (cmdlets.Any()) + var cmdlets = GetCmdletsFilesInFolder(directory); + if (!cmdlets.Any()) continue; + + foreach (var cmdletFileName in cmdlets) { - foreach (var cmdletFileName in cmdlets) - { - var cmdletFileFullPath = Path.Combine(directory, Path.GetFileName(cmdletFileName)); + var cmdletFileFullPath = Path.Combine(directory, Path.GetFileName(cmdletFileName)); - if (File.Exists(cmdletFileFullPath)) - { - var proxy = -#if !NETSTANDARD - EnvironmentHelpers.CreateProxy(directory, out _appDomain); + if (!File.Exists(cmdletFileFullPath)) continue; + +// TODO: Remove IfDef +#if NETSTANDARD + var proxy = new CmdletBreakingChangeAttributeLoader(); #else - new CmdletBreakingChangeAttributeLoader(); + var proxy = EnvironmentHelpers.CreateProxy(directory, out _appDomain); #endif - var cmdletDataForModule = proxy.GetModuleBreakingChangeAttributes(cmdletFileFullPath); + var cmdletDataForModule = proxy.GetModuleBreakingChangeAttributes(cmdletFileFullPath); - //If there is nothing in this module just onctinue - if (cmdletDataForModule == null) - { - Console.WriteLine("No breaking change attributes found in module " + cmdletFileName); - continue; - } + //If there is nothing in this module just continue + if (cmdletDataForModule == null) + { + Console.WriteLine("No breaking change attributes found in module " + cmdletFileName); + continue; + } - if (cmdletFilter != null) - { - string output = string.Format("Before filter\nmodule cmdlet count: {0}\n", - cmdletDataForModule.CmdletList.Count); + if (cmdletFilter != null) + { + var output = string.Format("Before filter\nmodule cmdlet count: {0}\n", + cmdletDataForModule.CmdletList.Count); - output += string.Format("\nCmdlet file: {0}", cmdletFileFullPath); + output += string.Format("\nCmdlet file: {0}", cmdletFileFullPath); - cmdletDataForModule.FilterCmdlets(cmdletFilter); + cmdletDataForModule.FilterCmdlets(cmdletFilter); - output += string.Format("After filter\nmodule cmdlet count: {0}\n", - cmdletDataForModule.CmdletList.Count); + output += string.Format("After filter\nmodule cmdlet count: {0}\n", + cmdletDataForModule.CmdletList.Count); - foreach (var cmdlet in cmdletDataForModule.CmdletList) - { - output += string.Format("\n\tcmdlet - {0}", cmdlet.CmdletName); - } + foreach (var cmdlet in cmdletDataForModule.CmdletList) + { + output += string.Format("\n\tcmdlet - {0}", cmdlet.CmdletName); + } - Console.WriteLine(output); - } + Console.WriteLine(output); + } - LogBreakingChangesInModule(cmdletDataForModule, logger); + LogBreakingChangesInModule(cmdletDataForModule, logger); +// TODO: Remove IfDef code #if !NETSTANDARD - AppDomain.Unload(_appDomain); + AppDomain.Unload(_appDomain); #endif - } - } } } } @@ -182,21 +179,20 @@ public void Analyze(IEnumerable cmdletProbingDirs, Func GetCmdletsFilesInFolder(string folderName) + private static IEnumerable GetCmdletsFilesInFolder(string folderName) { var service = Path.GetFileName(folderName); @@ -217,7 +213,7 @@ static IEnumerable GetCmdletsFilesInFolder(string folderName) var parentDirectory = Directory.GetParent(psd1); var psd1FileName = Path.GetFileName(psd1); - PowerShell powershell = PowerShell.Create(); + var powershell = PowerShell.Create(); powershell.AddScript("Import-LocalizedData -BaseDirectory " + parentDirectory + " -FileName " + psd1FileName + " -BindingVariable ModuleMetadata; $ModuleMetadata.NestedModules"); @@ -226,24 +222,25 @@ static IEnumerable GetCmdletsFilesInFolder(string folderName) return cmdletResult.Select(c => c.ToString().Substring(2)); } - const string BREAKING_CHANGE_MODUE_HEADER_FORMAT_STRING = @"## Breaking changes in module {0}\n\n The following cmdlets were affected this release:\n\n"; - const string BREAKING_CHANGE_CMDLET_HEADER_FORMAT_STRING = @"**{0}**\n"; + private const string BreakingChangeModuleHeaderFormatString = @"## Breaking changes in module {0}\n\n The following cmdlets were affected this release:\n\n"; + private const string BreakingChangeCmdletHeaderFormatString = @"**{0}**\n"; - //Logs all the breaking changes in a module as a unit (all cmdlets in the same module appear contigously) - private void LogBreakingChangesInModule(BreakingChangeAttributesInModule moduleData, TextFileLogger logger) + //Logs all the breaking changes in a module as a unit (all cmdlets in the same module appear contiguously) + private static void LogBreakingChangesInModule(BreakingChangeAttributesInModule moduleData, TextFileLogger logger) { - string textForBreakingChangesInModule = string.Format(BREAKING_CHANGE_MODUE_HEADER_FORMAT_STRING, Path.GetFileName(moduleData.ModuleName)); + var textForBreakingChangesInModule = string.Format(BreakingChangeModuleHeaderFormatString, Path.GetFileName(moduleData.ModuleName)); - foreach (BreakingChangeAttributesInCmdlet cmdletData in moduleData.CmdletList) + foreach (var cmdletData in moduleData.CmdletList) { - textForBreakingChangesInModule += string.Format(BREAKING_CHANGE_CMDLET_HEADER_FORMAT_STRING, cmdletData.CmdletName); + textForBreakingChangesInModule += string.Format(BreakingChangeCmdletHeaderFormatString, cmdletData.CmdletName); +// TODO: Remove IfDef code #if !NETSTANDARD foreach (GenericBreakingChangeAttribute attribute in cmdletData.BreakingChangeAttributes) { textForBreakingChangesInModule += attribute.GetBreakingChangeTextFromAttribute(cmdletData.CmdletType, true) + "\n\n"; } #endif - } + } //Now that we have the text, add it to the log file logger.LogMessage(textForBreakingChangesInModule); diff --git a/tools/StaticAnalysis/BreakingChangeAttributesAnalyzer/CmdletBreakingChangeAttributeLoader.cs b/tools/StaticAnalysis/BreakingChangeAttributesAnalyzer/CmdletBreakingChangeAttributeLoader.cs index 095f1fb5f781..8214e724c498 100644 --- a/tools/StaticAnalysis/BreakingChangeAttributesAnalyzer/CmdletBreakingChangeAttributeLoader.cs +++ b/tools/StaticAnalysis/BreakingChangeAttributesAnalyzer/CmdletBreakingChangeAttributeLoader.cs @@ -18,6 +18,7 @@ using System.Linq; using System.Reflection; using System.Management.Automation; +// TODO: Remove IfDef code #if !NETSTANDARD using Microsoft.WindowsAzure.Commands.Common.CustomAttributes; #endif @@ -32,7 +33,7 @@ public class BreakingChangeAttributesInModule public void FilterCmdlets(Func cmdletFilter) { - CmdletList = CmdletList.Where((cmdlet) => cmdletFilter(cmdlet.CmdletName)).ToList(); + CmdletList = CmdletList.Where(cmdlet => cmdletFilter(cmdlet.CmdletName)).ToList(); } } @@ -40,6 +41,7 @@ public class BreakingChangeAttributesInCmdlet { public Type CmdletType { get; set; } public string CmdletName { get; set; } +// TODO: Remove IfDef code #if !NETSTANDARD public List BreakingChangeAttributes { get; set; } #endif @@ -54,7 +56,7 @@ public class CmdletBreakingChangeAttributeLoader : MarshalByRefObject /// public BreakingChangeAttributesInModule GetModuleBreakingChangeAttributes(string assemblyPath) { - List results = new List(); + var results = new List(); try { @@ -62,6 +64,7 @@ public BreakingChangeAttributesInModule GetModuleBreakingChangeAttributes(string foreach (var type in assembly.GetCmdletTypes()) { var cmdlet = type.GetAttribute(); +// TODO: Remove IfDef code #if !NETSTANDARD var attributes = type.GetAttributes(); @@ -71,10 +74,11 @@ public BreakingChangeAttributesInModule GetModuleBreakingChangeAttributes(string { CmdletType = type, CmdletName = cmdlet.VerbName + "-" + cmdlet.NounName, +// TODO: Remove IfDef code #if !NETSTANDARD BreakingChangeAttributes = attributes.ToList() #endif - }; + }; results.Add(cmdletMetadata); } @@ -84,17 +88,14 @@ public BreakingChangeAttributesInModule GetModuleBreakingChangeAttributes(string throw ex; } - if (results.Count() > 0) - { - var attributesInTheModule = new BreakingChangeAttributesInModule(); + if (!results.Any()) return null; - attributesInTheModule.ModuleName = assemblyPath; - attributesInTheModule.CmdletList = results; - return attributesInTheModule; - } else + var attributesInTheModule = new BreakingChangeAttributesInModule { - return null; - } + ModuleName = assemblyPath, CmdletList = results + }; + + return attributesInTheModule; } } } diff --git a/tools/StaticAnalysis/DependencyAnalyzer/DependencyAnalyzer.cs b/tools/StaticAnalysis/DependencyAnalyzer/DependencyAnalyzer.cs index a466c569f972..967c7b6f4a69 100644 --- a/tools/StaticAnalysis/DependencyAnalyzer/DependencyAnalyzer.cs +++ b/tools/StaticAnalysis/DependencyAnalyzer/DependencyAnalyzer.cs @@ -31,14 +31,14 @@ namespace StaticAnalysis.DependencyAnalyzer ///
public class DependencyAnalyzer : IStaticAnalyzer { - const int NoAssemblyVersionEvidence = 1000; - const int ReferenceDoesNotMatchAssemblyVersion = 1010; - const int ExtraAssemblyRecord = 2000; - const int MissingAssemblyRecord = 3000; - const int AssemblyVersionFileVersionMismatch = 7000; - const int CommonAuthenticationMismatch = 7010; - - static List FrameworkAssemblies = new List + private const int NoAssemblyVersionEvidence = 1000; + private const int ReferenceDoesNotMatchAssemblyVersion = 1010; + private const int ExtraAssemblyRecord = 2000; + private const int MissingAssemblyRecord = 3000; + private const int AssemblyVersionFileVersionMismatch = 7000; + private const int CommonAuthenticationMismatch = 7010; + + private static readonly List FrameworkAssemblies = new List { "Microsoft.CSharp", "Microsoft.Management.Infrastructure", @@ -48,13 +48,14 @@ public class DependencyAnalyzer : IStaticAnalyzer "WindowsBase" }; - private Dictionary _assemblies = + private readonly Dictionary _assemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); - private Dictionary _sharedAssemblyReferences = + private readonly Dictionary _sharedAssemblyReferences = new Dictionary(new AssemblyNameComparer()); - private Dictionary _identicalSharedAssemblies = + private readonly Dictionary _identicalSharedAssemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); +// TODO: Remove IfDef code #if !NETSTANDARD private AppDomain _testDomain; #endif @@ -98,7 +99,7 @@ public void Analyze(IEnumerable directories, IEnumerable modules { if (modulesToAnalyze != null && modulesToAnalyze.Any() && - !modulesToAnalyze.Where(m => directoryPath.EndsWith(m)).Any()) + !modulesToAnalyze.Any(m => directoryPath.EndsWith(m))) { continue; } @@ -132,7 +133,7 @@ private AssemblyRecord CreateAssemblyRecord(string path) { var assembly = LoadByReflectionFromFile(fullPath); var versionInfo = FileVersionInfo.GetVersionInfo(fullPath); - result = new AssemblyRecord() + result = new AssemblyRecord { AssemblyName = assembly.GetName(), AssemblyFileMajorVersion = versionInfo.FileMajorPart, @@ -158,36 +159,33 @@ private bool AddSharedAssembly(AssemblyRecord assembly) if (_sharedAssemblyReferences.ContainsKey(assembly.AssemblyName)) { var stored = _sharedAssemblyReferences[assembly.AssemblyName]; - if (!assembly.Equals(stored) && !(IsFrameworkAssembly(assembly.AssemblyName) && assembly.Version.Major <= 4)) + if (assembly.Equals(stored) || IsFrameworkAssembly(assembly.AssemblyName) && assembly.Version.Major <= 4) return true; + + _sharedConflictLogger.LogRecord(new SharedAssemblyConflict { - _sharedConflictLogger.LogRecord(new SharedAssemblyConflict + AssemblyName = assembly.Name, + AssemblyPathsAndFileVersions = new List> { - AssemblyName = assembly.Name, - AssemblyPathsAndFileVersions = new List>() - { - new Tuple(assembly.Location, new Version(assembly.AssemblyFileMajorVersion, - assembly.AssemblyFileMinorVersion)), - new Tuple(stored.Location, new Version(stored.AssemblyFileMajorVersion, - stored.AssemblyFileMinorVersion)) - - }, - AssemblyVersion = assembly.Version, - Severity = 0, - ProblemId = AssemblyVersionFileVersionMismatch, - Description = "Shared assembly conflict, shared assemblies with the same assembly " + - "version have differing file versions", - Remediation = string.Format("Update the assembly reference for {0} in one of the " + - "referring assemblies", assembly.Name) - }); + new Tuple(assembly.Location, new Version(assembly.AssemblyFileMajorVersion, + assembly.AssemblyFileMinorVersion)), + new Tuple(stored.Location, new Version(stored.AssemblyFileMajorVersion, + stored.AssemblyFileMinorVersion)) - return false; - } - } - else - { - _sharedAssemblyReferences[assembly.AssemblyName] = assembly; + }, + AssemblyVersion = assembly.Version, + Severity = 0, + ProblemId = AssemblyVersionFileVersionMismatch, + Description = "Shared assembly conflict, shared assemblies with the same assembly " + + "version have differing file versions", + Remediation = string.Format("Update the assembly reference for {0} in one of the " + + "referring assemblies", assembly.Name) + }); + + return false; } + _sharedAssemblyReferences[assembly.AssemblyName] = assembly; + return true; } @@ -207,36 +205,33 @@ private bool AddSharedAssemblyExactVersion(AssemblyRecord record) if (_identicalSharedAssemblies.ContainsKey(record.Name)) { var stored = _identicalSharedAssemblies[record.Name]; - if (!record.Equals(stored) && !(IsFrameworkAssembly(record.AssemblyName))) + if (record.Equals(stored) || IsFrameworkAssembly(record.AssemblyName)) return true; + + _sharedConflictLogger.LogRecord(new SharedAssemblyConflict { - _sharedConflictLogger.LogRecord(new SharedAssemblyConflict + AssemblyName = record.Name, + AssemblyVersion = record.Version, + Severity = 0, + ProblemId = CommonAuthenticationMismatch, + AssemblyPathsAndFileVersions = new List> { - AssemblyName = record.Name, - AssemblyVersion = record.Version, - Severity = 0, - ProblemId = CommonAuthenticationMismatch, - AssemblyPathsAndFileVersions = new List>() - { - new Tuple(record.Location, new Version(record.AssemblyFileMajorVersion, - record.AssemblyFileMinorVersion)), - new Tuple(stored.Location, new Version(stored.AssemblyFileMajorVersion, - stored.AssemblyFileMinorVersion)), - }, - Description = string.Format("Assembly {0} has multiple versions as specified in 'Target'", + new Tuple(record.Location, new Version(record.AssemblyFileMajorVersion, + record.AssemblyFileMinorVersion)), + new Tuple(stored.Location, new Version(stored.AssemblyFileMajorVersion, + stored.AssemblyFileMinorVersion)), + }, + Description = string.Format("Assembly {0} has multiple versions as specified in 'Target'", record.Name), - Remediation = string.Format("Ensure that all packages reference exactly the same package " + - "version of {0}", record.Name) + Remediation = string.Format("Ensure that all packages reference exactly the same package " + + "version of {0}", record.Name) - }); + }); - return false; - } - } - else - { - _identicalSharedAssemblies[record.Name] = record; + return false; } + _identicalSharedAssemblies[record.Name] = record; + return true; } @@ -255,15 +250,16 @@ private void ProcessDirectory(string directoryPath) { var savedDirectory = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(directoryPath); - _loader = -#if !NETSTANDARD - EnvironmentHelpers.CreateProxy(directoryPath, out _testDomain); + +// TODO: Remove IfDef +#if NETSTANDARD + _loader = new AssemblyLoader(); #else - new AssemblyLoader(); + _loader = EnvironmentHelpers.CreateProxy(directoryPath, out _testDomain); #endif foreach (var file in Directory.GetFiles(directoryPath).Where(file => file.EndsWith(".dll"))) { - AssemblyRecord assembly = CreateAssemblyRecord(file); + var assembly = CreateAssemblyRecord(file); _assemblies[assembly.Name] = assembly; if (RequiresExactVersionMatch(assembly)) { @@ -305,6 +301,7 @@ private void ProcessDirectory(string directoryPath) FindExtraAssemblies(); +// TODO: Remove IfDef code #if !NETSTANDARD AppDomain.Unload(_testDomain); #endif @@ -318,25 +315,31 @@ private static bool IsCommandAssembly(AssemblyRecord assembly) private void FindExtraAssemblies() { - if (_assemblies.Values.Any(a => !IsCommandAssembly(a) && (a.ReferencingAssembly == null - || a.ReferencingAssembly.Count == 0 || !a.GetAncestors().Any(IsCommandAssembly)))) + if (!_assemblies.Values.Any(a => + !IsCommandAssembly(a) + && (a.ReferencingAssembly == null + || a.ReferencingAssembly.Count == 0 + || !a.GetAncestors().Any(IsCommandAssembly)))) { - foreach ( - var assembly in - _assemblies.Values.Where(a => !IsCommandAssembly(a) && (a.ReferencingAssembly == null || - a.ReferencingAssembly.Count == 0 || !a.GetAncestors().Any(IsCommandAssembly)))) + return; + } + + foreach (var assembly in _assemblies.Values.Where(a => + !IsCommandAssembly(a) + && (a.ReferencingAssembly == null + || a.ReferencingAssembly.Count == 0 + || !a.GetAncestors().Any(IsCommandAssembly)))) + { + _extraAssemblyLogger.LogRecord(new ExtraAssembly { - _extraAssemblyLogger.LogRecord(new ExtraAssembly - { - AssemblyName = assembly.Name, - Severity = 2, - ProblemId = ExtraAssemblyRecord, - Description = string.Format("Assembly {0} is not referenced from any cmdlets assembly", + AssemblyName = assembly.Name, + Severity = 2, + ProblemId = ExtraAssemblyRecord, + Description = string.Format("Assembly {0} is not referenced from any cmdlets assembly", assembly.Name), - Remediation = string.Format("Remove assembly {0} from the project and regenerate the Wix " + - "file", assembly.Name) - }); - } + Remediation = string.Format("Remove assembly {0} from the project and regenerate the Wix " + + "file", assembly.Name) + }); } } @@ -353,7 +356,7 @@ private void CheckAssemblyReference(AssemblyName reference, AssemblyRecord paren { Logger.WriteWarning("{0}.dll has reference to assembly {1} without any version specification.", parent.Name, reference.Name); - _versionConflictLogger.LogRecord(new AssemblyVersionConflict() + _versionConflictLogger.LogRecord(new AssemblyVersionConflict { AssemblyName = reference.Name, ActualVersion = stored.Version, @@ -372,7 +375,7 @@ private void CheckAssemblyReference(AssemblyName reference, AssemblyRecord paren else if (_isNetcore && stored.Version < reference.Version) { var minVersion = (stored.Version < reference.Version) ? stored.Version : reference.Version; - _versionConflictLogger.LogRecord(new AssemblyVersionConflict() + _versionConflictLogger.LogRecord(new AssemblyVersionConflict { AssemblyName = reference.Name, ActualVersion = stored.Version, diff --git a/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs b/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs index 82493b6d1640..c6da38179379 100644 --- a/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs +++ b/tools/StaticAnalysis/HelpAnalyzer/HelpAnalyzer.cs @@ -31,8 +31,8 @@ namespace StaticAnalysis.HelpAnalyzer ///
public class HelpAnalyzer : IStaticAnalyzer { - const int MissingHelp = 6050; - const int MissingHelpFile = 6000; + private const int MissingHelp = 6050; + private const int MissingHelpFile = 6000; public HelpAnalyzer() { Name = "Help Analyzer"; @@ -40,6 +40,7 @@ public HelpAnalyzer() public AnalysisLogger Logger { get; set; } public string Name { get; private set; } +// TODO: Remove IfDef code #if !NETSTANDARD private AppDomain _appDomain; #endif @@ -95,13 +96,13 @@ public void Analyze(IEnumerable scopes, IEnumerable modulesToAna { if (modulesToAnalyze != null && modulesToAnalyze.Any() && - !modulesToAnalyze.Where(m => directory.EndsWith(m)).Any()) + !modulesToAnalyze.Any(m => directory.EndsWith(m))) { continue; } var dirs = Directory.EnumerateDirectories(directory); - if (dirs != null && dirs.Any((d) => string.Equals(Path.GetFileName(d), "help", StringComparison.OrdinalIgnoreCase))) + if (dirs != null && dirs.Any(d => string.Equals(Path.GetFileName(d), "help", StringComparison.OrdinalIgnoreCase))) { AnalyzeMarkdownHelp(scopes, directory, helpLogger, processedHelpFiles, savedDirectory); } @@ -136,41 +137,41 @@ private void AnalyzeMamlHelp( var helpFiles = Directory.EnumerateFiles(directory, "*.dll-Help.xml") .Where(f => !processedHelpFiles.Contains(Path.GetFileName(f), StringComparer.OrdinalIgnoreCase)).ToList(); - if (helpFiles.Any()) + if (!helpFiles.Any()) return; + + Directory.SetCurrentDirectory(directory); + foreach (var helpFile in helpFiles) { - Directory.SetCurrentDirectory(directory); - foreach (var helpFile in helpFiles) + var cmdletFile = helpFile.Substring(0, helpFile.Length - "-Help.xml".Length); + var helpFileName = Path.GetFileName(helpFile); + var cmdletFileName = Path.GetFileName(cmdletFile); + if (!File.Exists(cmdletFile)) continue; + + processedHelpFiles.Add(helpFileName); + helpLogger.Decorator.AddDecorator(h => { - var cmdletFile = helpFile.Substring(0, helpFile.Length - "-Help.xml".Length); - var helpFileName = Path.GetFileName(helpFile); - var cmdletFileName = Path.GetFileName(cmdletFile); - if (File.Exists(cmdletFile)) - { - processedHelpFiles.Add(helpFileName); - helpLogger.Decorator.AddDecorator((h) => - { - h.HelpFile = helpFileName; - h.Assembly = cmdletFileName; - }, "Cmdlet"); - var proxy = -#if !NETSTANDARD - EnvironmentHelpers.CreateProxy(directory, out _appDomain); + h.HelpFile = helpFileName; + h.Assembly = cmdletFileName; + }, "Cmdlet"); + +// TODO: Remove IfDef +#if NETSTANDARD + var proxy = new CmdletLoader(); #else - new CmdletLoader(); + var proxy = EnvironmentHelpers.CreateProxy(directory, out _appDomain); #endif - var module = proxy.GetModuleMetadata(cmdletFile); - var cmdlets = module.Cmdlets; - var helpRecords = CmdletHelpParser.GetHelpTopics(helpFile, helpLogger); - ValidateHelpRecords(cmdlets, helpRecords, helpLogger); - helpLogger.Decorator.Remove("Cmdlet"); + var module = proxy.GetModuleMetadata(cmdletFile); + var cmdlets = module.Cmdlets; + var helpRecords = CmdletHelpParser.GetHelpTopics(helpFile, helpLogger); + ValidateHelpRecords(cmdlets, helpRecords, helpLogger); + helpLogger.Decorator.Remove("Cmdlet"); +// TODO: Remove IfDef code #if !NETSTANDARD - AppDomain.Unload(_appDomain); + AppDomain.Unload(_appDomain); #endif - } - } - - Directory.SetCurrentDirectory(savedDirectory); } + + Directory.SetCurrentDirectory(savedDirectory); } private void AnalyzeMarkdownHelp( @@ -199,79 +200,77 @@ private void AnalyzeMarkdownHelp( return; } - var helpFiles = Directory.EnumerateFiles(helpFolder, "*.md").Select(f => Path.GetFileNameWithoutExtension(f)).ToList(); - if (helpFiles.Any()) + var helpFiles = Directory.EnumerateFiles(helpFolder, "*.md").Select(Path.GetFileNameWithoutExtension).ToList(); + if (!helpFiles.Any()) return; + + Directory.SetCurrentDirectory(directory); + var manifestFiles = Directory.EnumerateFiles(directory, "*.psd1").ToList(); + if (manifestFiles.Count > 1) + { + manifestFiles = manifestFiles.Where(f => Path.GetFileName(f).IndexOf(service) >= 0).ToList(); + } + + if (manifestFiles.Count == 0) + { + return; + } + + var psd1 = manifestFiles.FirstOrDefault(); + var parentDirectory = Directory.GetParent(psd1).FullName; + var psd1FileName = Path.GetFileName(psd1); + var powershell = PowerShell.Create(); + powershell.AddScript("Import-LocalizedData -BaseDirectory " + parentDirectory + + " -FileName " + psd1FileName + + " -BindingVariable ModuleMetadata; $ModuleMetadata.NestedModules; $ModuleMetadata.RequiredModules | % { $_[\"ModuleName\"] };"); + var cmdletResult = powershell.Invoke(); + var nestedModules = cmdletResult.Where(c => c.ToString().StartsWith(".")).Select(c => c.ToString().Substring(2)); + var requiredModules = cmdletResult.Where(c => !c.ToString().StartsWith(".")).Select(c => c.ToString()).ToList(); + if (nestedModules.Any()) { Directory.SetCurrentDirectory(directory); - var manifestFiles = Directory.EnumerateFiles(directory, "*.psd1").ToList(); - if (manifestFiles.Count > 1) - { - manifestFiles = manifestFiles.Where(f => Path.GetFileName(f).IndexOf(service) >= 0).ToList(); - } - if (manifestFiles.Count == 0) - { - return; - } + requiredModules = requiredModules.Join(scopes, + module => 1, + dir => 1, + (module, dir) => Path.Combine(dir, module)) + .Where(Directory.Exists) + .ToList(); - var psd1 = manifestFiles.FirstOrDefault(); - var parentDirectory = Directory.GetParent(psd1).FullName; - var psd1FileName = Path.GetFileName(psd1); - IEnumerable nestedModules = null; - List requiredModules = null; - PowerShell powershell = PowerShell.Create(); - powershell.AddScript("Import-LocalizedData -BaseDirectory " + parentDirectory + - " -FileName " + psd1FileName + - " -BindingVariable ModuleMetadata; $ModuleMetadata.NestedModules; $ModuleMetadata.RequiredModules | % { $_[\"ModuleName\"] };"); - var cmdletResult = powershell.Invoke(); - nestedModules = cmdletResult.Where(c => c.ToString().StartsWith(".")).Select(c => c.ToString().Substring(2)); - requiredModules = cmdletResult.Where(c => !c.ToString().StartsWith(".")).Select(c => c.ToString()).ToList(); - if (nestedModules.Any()) + requiredModules.Add(directory); + var allCmdlets = new List(); + foreach (var nestedModule in nestedModules) { - Directory.SetCurrentDirectory(directory); - - requiredModules = requiredModules.Join(scopes, - module => 1, - dir => 1, - (module, dir) => Path.Combine(dir, module)) - .Where(f => Directory.Exists(f)) - .ToList(); + var assemblyFile = Directory.GetFiles(parentDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault(); + if (!File.Exists(assemblyFile)) continue; - requiredModules.Add(directory); - List allCmdlets = new List(); - foreach (var nestedModule in nestedModules) + var assemblyFileName = Path.GetFileName(assemblyFile); + helpLogger.Decorator.AddDecorator(h => { - var assemblyFile = Directory.GetFiles(parentDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault(); - if (File.Exists(assemblyFile)) - { - var assemblyFileName = Path.GetFileName(assemblyFile); - helpLogger.Decorator.AddDecorator((h) => - { - h.HelpFile = assemblyFileName; - h.Assembly = assemblyFileName; - }, "Cmdlet"); - processedHelpFiles.Add(assemblyFileName); - var proxy = -#if !NETSTANDARD - EnvironmentHelpers.CreateProxy(directory, out _appDomain); + h.HelpFile = assemblyFileName; + h.Assembly = assemblyFileName; + }, "Cmdlet"); + processedHelpFiles.Add(assemblyFileName); +// TODO: Remove IfDef +#if NETSTANDARD + var proxy = new CmdletLoader(); #else - new CmdletLoader(); + var proxy = EnvironmentHelpers.CreateProxy(directory, out _appDomain); #endif - var module = proxy.GetModuleMetadata(assemblyFile, requiredModules); - var cmdlets = module.Cmdlets; - allCmdlets.AddRange(cmdlets); - helpLogger.Decorator.Remove("Cmdlet"); + var module = proxy.GetModuleMetadata(assemblyFile, requiredModules); + var cmdlets = module.Cmdlets; + allCmdlets.AddRange(cmdlets); + helpLogger.Decorator.Remove("Cmdlet"); +// TODO: Remove IfDef code #if !NETSTANDARD - AppDomain.Unload(_appDomain); + AppDomain.Unload(_appDomain); #endif - } - } - - ValidateHelpRecords(allCmdlets, helpFiles, helpLogger); } - Directory.SetCurrentDirectory(savedDirectory); + ValidateHelpRecords(allCmdlets, helpFiles, helpLogger); } + + Directory.SetCurrentDirectory(savedDirectory); + } private void ValidateHelpRecords(IList cmdlets, IList helpRecords, diff --git a/tools/StaticAnalysis/SignatureVerifier/SignatureVerifier.cs b/tools/StaticAnalysis/SignatureVerifier/SignatureVerifier.cs index 921badf79aa2..2c00860d4269 100644 --- a/tools/StaticAnalysis/SignatureVerifier/SignatureVerifier.cs +++ b/tools/StaticAnalysis/SignatureVerifier/SignatureVerifier.cs @@ -28,14 +28,15 @@ namespace StaticAnalysis.SignatureVerifier { public class SignatureVerifier : IStaticAnalyzer { +// TODO: Remove IfDef code #if !NETSTANDARD private AppDomain _appDomain; #endif - string signatureIssueReportLoggerName; + private readonly string _signatureIssueReportLoggerName; public SignatureVerifier() { Name = "Signature Verifier"; - signatureIssueReportLoggerName = "SignatureIssues.csv"; + _signatureIssueReportLoggerName = "SignatureIssues.csv"; } public AnalysisLogger Logger { get; set; } @@ -63,9 +64,9 @@ public void Analyze(IEnumerable cmdletProbingDirs, { var savedDirectory = Directory.GetCurrentDirectory(); var processedHelpFiles = new List(); - var issueLogger = Logger.CreateLogger(signatureIssueReportLoggerName); + var issueLogger = Logger.CreateLogger(_signatureIssueReportLoggerName); - List probingDirectories = new List(); + var probingDirectories = new List(); if (directoryFilter != null) { @@ -83,7 +84,7 @@ public void Analyze(IEnumerable cmdletProbingDirs, { if (modulesToAnalyze != null && modulesToAnalyze.Any() && - !modulesToAnalyze.Where(m => directory.EndsWith(m)).Any()) + !modulesToAnalyze.Any(m => directory.EndsWith(m))) { continue; } @@ -105,7 +106,7 @@ public void Analyze(IEnumerable cmdletProbingDirs, var psd1FileName = Path.GetFileName(psd1); IEnumerable nestedModules = null; List requiredModules = null; - PowerShell powershell = PowerShell.Create(); + var powershell = PowerShell.Create(); powershell.AddScript("Import-LocalizedData -BaseDirectory " + parentDirectory + " -FileName " + psd1FileName + " -BindingVariable ModuleMetadata; $ModuleMetadata.NestedModules; $ModuleMetadata.RequiredModules | % { $_[\"ModuleName\"] };"); @@ -113,203 +114,201 @@ public void Analyze(IEnumerable cmdletProbingDirs, nestedModules = cmdletResult.Where(c => c.ToString().StartsWith(".")).Select(c => c.ToString().Substring(2)); requiredModules = cmdletResult.Where(c => !c.ToString().StartsWith(".")).Select(c => c.ToString()).ToList(); - if (nestedModules.Any()) - { - Directory.SetCurrentDirectory(directory); + if (!nestedModules.Any()) continue; - requiredModules = requiredModules.Join(cmdletProbingDirs, - module => 1, - dir => 1, - (module, dir) => Path.Combine(dir, module)) - .Where(f => Directory.Exists(f)) - .ToList(); + Directory.SetCurrentDirectory(directory); - requiredModules.Add(directory); + requiredModules = requiredModules.Join(cmdletProbingDirs, + module => 1, + dir => 1, + (module, dir) => Path.Combine(dir, module)) + .Where(Directory.Exists) + .ToList(); - foreach (var nestedModule in nestedModules) - { - var assemblyFile = Directory.GetFiles(parentDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault(); - if (File.Exists(assemblyFile)) - { - issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = assemblyFile, "AssemblyFileName"); - processedHelpFiles.Add(assemblyFile); - var proxy = -#if !NETSTANDARD - EnvironmentHelpers.CreateProxy(directory, out _appDomain); + requiredModules.Add(directory); + + foreach (var nestedModule in nestedModules) + { + var assemblyFile = Directory.GetFiles(parentDirectory, nestedModule, SearchOption.AllDirectories).FirstOrDefault(); + if (!File.Exists(assemblyFile)) continue; + + issueLogger.Decorator.AddDecorator(a => a.AssemblyFileName = assemblyFile, "AssemblyFileName"); + processedHelpFiles.Add(assemblyFile); +// TODO: Remove IfDef +#if NETSTANDARD + var proxy = new CmdletLoader(); #else - new CmdletLoader(); + var proxy = EnvironmentHelpers.CreateProxy(directory, out _appDomain); #endif - var module = proxy.GetModuleMetadata(assemblyFile, requiredModules); - var cmdlets = module.Cmdlets; - - if (cmdletFilter != null) - { - cmdlets = cmdlets.Where((cmdlet) => cmdletFilter(cmdlet.Name)).ToList(); - } + var module = proxy.GetModuleMetadata(assemblyFile, requiredModules); + var cmdlets = module.Cmdlets; - foreach (var cmdlet in cmdlets) - { - Logger.WriteMessage("Processing cmdlet '{0}'", cmdlet.ClassName); - string defaultRemediation = "Determine if the cmdlet should implement ShouldProcess and " + - "if so determine if it should implement Force / ShouldContinue"; - if (!cmdlet.SupportsShouldProcess && cmdlet.HasForceSwitch) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 0, - problemId: SignatureProblemId.ForceWithoutShouldProcessAttribute, - description: string.Format("{0} Has -Force parameter but does not set the SupportsShouldProcess " + - "property to true in the Cmdlet attribute.", cmdlet.Name), - remediation: defaultRemediation); - } - if (!cmdlet.SupportsShouldProcess && cmdlet.ConfirmImpact != ConfirmImpact.Medium) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 2, - problemId: SignatureProblemId.ConfirmLeveleWithNoShouldProcess, - description: - string.Format("{0} Changes the ConfirmImpact but does not set the " + - "SupportsShouldProcess property to true in the cmdlet attribute.", - cmdlet.Name), - remediation: defaultRemediation); - } - if (!cmdlet.SupportsShouldProcess && cmdlet.IsShouldProcessVerb) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 1, - problemId: SignatureProblemId.ActionIndicatesShouldProcess, - description: - string.Format( - "{0} Does not support ShouldProcess but the cmdlet verb {1} indicates that it should.", - cmdlet.Name, cmdlet.VerbName), - remediation: defaultRemediation); - } - if (cmdlet.ConfirmImpact != ConfirmImpact.Medium) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 2, - problemId: SignatureProblemId.ConfirmLevelChange, - description: - string.Format("{0} changes the confirm impact. Please ensure that the " + - "change in ConfirmImpact is justified", cmdlet.Name), - remediation: - "Verify that ConfirmImpact is changed appropriately by the cmdlet. " + - "It is very rare for a cmdlet to change the ConfirmImpact."); - } - if (!cmdlet.IsApprovedVerb) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 1, - problemId: SignatureProblemId.CmdletWithUnapprovedVerb, - description: - string.Format( - "{0} uses the verb '{1}', which is not on the list of approved " + - "verbs for PowerShell commands. Use the cmdlet 'Get-Verb' to see " + - "the full list of approved verbs and consider renaming the cmdlet.", - cmdlet.Name, cmdlet.VerbName), - remediation: "Consider renaming the cmdlet to use an approved verb for PowerShell."); - } + if (cmdletFilter != null) + { + cmdlets = cmdlets.Where(cmdlet => cmdletFilter(cmdlet.Name)).ToList(); + } - if (!cmdlet.HasSingularNoun) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 1, - problemId: SignatureProblemId.CmdletWithPluralNoun, - description: - string.Format( - "{0} uses the noun '{1}', which does not follow the enforced " + - "naming convention of using a singular noun for a cmdlet name.", - cmdlet.Name, cmdlet.NounName), - remediation: "Consider using a singular noun for the cmdlet name."); - } + foreach (var cmdlet in cmdlets) + { + Logger.WriteMessage("Processing cmdlet '{0}'", cmdlet.ClassName); + const string defaultRemediation = "Determine if the cmdlet should implement ShouldProcess and " + + "if so determine if it should implement Force / ShouldContinue"; + if (!cmdlet.SupportsShouldProcess && cmdlet.HasForceSwitch) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 0, + problemId: SignatureProblemId.ForceWithoutShouldProcessAttribute, + description: string.Format("{0} Has -Force parameter but does not set the SupportsShouldProcess " + + "property to true in the Cmdlet attribute.", cmdlet.Name), + remediation: defaultRemediation); + } + if (!cmdlet.SupportsShouldProcess && cmdlet.ConfirmImpact != ConfirmImpact.Medium) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 2, + problemId: SignatureProblemId.ConfirmLeveleWithNoShouldProcess, + description: + string.Format("{0} Changes the ConfirmImpact but does not set the " + + "SupportsShouldProcess property to true in the cmdlet attribute.", + cmdlet.Name), + remediation: defaultRemediation); + } + if (!cmdlet.SupportsShouldProcess && cmdlet.IsShouldProcessVerb) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 1, + problemId: SignatureProblemId.ActionIndicatesShouldProcess, + description: + string.Format( + "{0} Does not support ShouldProcess but the cmdlet verb {1} indicates that it should.", + cmdlet.Name, cmdlet.VerbName), + remediation: defaultRemediation); + } + if (cmdlet.ConfirmImpact != ConfirmImpact.Medium) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 2, + problemId: SignatureProblemId.ConfirmLevelChange, + description: + string.Format("{0} changes the confirm impact. Please ensure that the " + + "change in ConfirmImpact is justified", cmdlet.Name), + remediation: + "Verify that ConfirmImpact is changed appropriately by the cmdlet. " + + "It is very rare for a cmdlet to change the ConfirmImpact."); + } + if (!cmdlet.IsApprovedVerb) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 1, + problemId: SignatureProblemId.CmdletWithUnapprovedVerb, + description: + string.Format( + "{0} uses the verb '{1}', which is not on the list of approved " + + "verbs for PowerShell commands. Use the cmdlet 'Get-Verb' to see " + + "the full list of approved verbs and consider renaming the cmdlet.", + cmdlet.Name, cmdlet.VerbName), + remediation: "Consider renaming the cmdlet to use an approved verb for PowerShell."); + } - if (!cmdlet.OutputTypes.Any()) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 1, - problemId: SignatureProblemId.CmdletWithNoOutputType, - description: - string.Format( - "Cmdlet '{0}' has no defined output type.", cmdlet.Name), - remediation: "Add an OutputType attribute that declares the type of the object(s) returned " + - "by this cmdlet. If this cmdlet returns no output, please set the output " + - "type to 'bool' and make sure to implement the 'PassThru' parameter."); - } + if (!cmdlet.HasSingularNoun) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 1, + problemId: SignatureProblemId.CmdletWithPluralNoun, + description: + string.Format( + "{0} uses the noun '{1}', which does not follow the enforced " + + "naming convention of using a singular noun for a cmdlet name.", + cmdlet.Name, cmdlet.NounName), + remediation: "Consider using a singular noun for the cmdlet name."); + } - foreach (var parameter in cmdlet.GetParametersWithPluralNoun()) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 1, - problemId: SignatureProblemId.ParameterWithPluralNoun, - description: - string.Format( - "Parameter {0} of cmdlet {1} does not follow the enforced " + - "naming convention of using a singular noun for a parameter name.", - parameter.Name, cmdlet.Name), - remediation: "Consider using a singular noun for the parameter name."); - } + if (!cmdlet.OutputTypes.Any()) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 1, + problemId: SignatureProblemId.CmdletWithNoOutputType, + description: + string.Format( + "Cmdlet '{0}' has no defined output type.", cmdlet.Name), + remediation: "Add an OutputType attribute that declares the type of the object(s) returned " + + "by this cmdlet. If this cmdlet returns no output, please set the output " + + "type to 'bool' and make sure to implement the 'PassThru' parameter."); + } - foreach (var parameterSet in cmdlet.ParameterSets) - { - if (parameterSet.Name.Contains(" ")) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 1, - problemId: SignatureProblemId.ParameterSetWithSpace, - description: - string.Format( - "Parameter set '{0}' of cmdlet '{1}' contains a space, which " + - "is discouraged for PowerShell parameter sets.", - parameterSet.Name, cmdlet.Name), - remediation: "Remove the space(s) in the parameter set name."); - } + foreach (var parameter in cmdlet.GetParametersWithPluralNoun()) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 1, + problemId: SignatureProblemId.ParameterWithPluralNoun, + description: + string.Format( + "Parameter {0} of cmdlet {1} does not follow the enforced " + + "naming convention of using a singular noun for a parameter name.", + parameter.Name, cmdlet.Name), + remediation: "Consider using a singular noun for the parameter name."); + } - if (parameterSet.Parameters.Any(p => p.Position >= 4)) - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 1, - problemId: SignatureProblemId.ParameterWithOutOfRangePosition, - description: - string.Format( - "Parameter set '{0}' of cmdlet '{1}' contains at least one parameter " + - "with a position larger than four, which is discouraged.", - parameterSet.Name, cmdlet.Name), - remediation: "Limit the number of positional parameters in a single parameter set to " + - "four or fewer."); - } - } + foreach (var parameterSet in cmdlet.ParameterSets) + { + if (parameterSet.Name.Contains(" ")) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 1, + problemId: SignatureProblemId.ParameterSetWithSpace, + description: + string.Format( + "Parameter set '{0}' of cmdlet '{1}' contains a space, which " + + "is discouraged for PowerShell parameter sets.", + parameterSet.Name, cmdlet.Name), + remediation: "Remove the space(s) in the parameter set name."); + } - if (cmdlet.ParameterSets.Count() > 2 && cmdlet.DefaultParameterSetName == "__AllParameterSets") - { - issueLogger.LogSignatureIssue( - cmdlet: cmdlet, - severity: 1, - problemId: SignatureProblemId.MultipleParameterSetsWithNoDefault, + if (parameterSet.Parameters.Any(p => p.Position >= 4)) + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 1, + problemId: SignatureProblemId.ParameterWithOutOfRangePosition, description: - string.Format( - "Cmdlet '{0}' has multiple parameter sets, but no defined default parameter set.", - cmdlet.Name), - remediation: "Define a default parameter set in the cmdlet attribute."); - } + string.Format( + "Parameter set '{0}' of cmdlet '{1}' contains at least one parameter " + + "with a position larger than four, which is discouraged.", + parameterSet.Name, cmdlet.Name), + remediation: "Limit the number of positional parameters in a single parameter set to " + + "four or fewer."); } + } -#if !NETSTANDARD - AppDomain.Unload(_appDomain); -#endif - issueLogger.Decorator.Remove("AssemblyFileName"); + if (cmdlet.ParameterSets.Count > 2 && cmdlet.DefaultParameterSetName == "__AllParameterSets") + { + issueLogger.LogSignatureIssue( + cmdlet: cmdlet, + severity: 1, + problemId: SignatureProblemId.MultipleParameterSetsWithNoDefault, + description: + string.Format( + "Cmdlet '{0}' has multiple parameter sets, but no defined default parameter set.", + cmdlet.Name), + remediation: "Define a default parameter set in the cmdlet attribute."); } } - Directory.SetCurrentDirectory(savedDirectory); +// TODO: Remove IfDef code +#if !NETSTANDARD + AppDomain.Unload(_appDomain); +#endif + issueLogger.Decorator.Remove("AssemblyFileName"); } + Directory.SetCurrentDirectory(savedDirectory); } } } @@ -323,11 +322,11 @@ public AnalysisReport GetAnalysisReport() //TODO: in next sprint, more work is scheduled to add more enhancements to this tool. // this report is necessary when tests needs more informaiton on results of static analysis // more information will be added to this report - AnalysisReport analysisReport = new AnalysisReport(); - ReportLogger reportLog = Logger.GetReportLogger(signatureIssueReportLoggerName); + var analysisReport = new AnalysisReport(); + var reportLog = Logger.GetReportLogger(_signatureIssueReportLoggerName); if(reportLog.Records.Any()) { - foreach(IReportRecord rec in reportLog.Records) + foreach(var rec in reportLog.Records) { analysisReport.ProblemIdList.Add(rec.ProblemId); } diff --git a/tools/Tools.Common/Helpers/EnvironmentHelpers.cs b/tools/Tools.Common/Helpers/EnvironmentHelpers.cs index f9a515729d4e..fb8996e30018 100644 --- a/tools/Tools.Common/Helpers/EnvironmentHelpers.cs +++ b/tools/Tools.Common/Helpers/EnvironmentHelpers.cs @@ -18,6 +18,7 @@ namespace Tools.Common.Helpers { public static class EnvironmentHelpers { +// TODO: Remove IfDef code #if !NETSTANDARD /// /// Create a new AppDomain and create a remote instance of AssemblyLoader we can use there @@ -58,7 +59,7 @@ public static string GetDirectoryName(string path) throw new ArgumentNullException("path"); } - string result = path.TrimEnd('\\'); + var result = path.TrimEnd('\\'); var lastSlash = result.LastIndexOf("\\"); if (lastSlash > 0) { diff --git a/tools/Tools.Common/Loaders/AssemblyLoader.cs b/tools/Tools.Common/Loaders/AssemblyLoader.cs index 5627df1325af..d249b0bc5f9f 100644 --- a/tools/Tools.Common/Loaders/AssemblyLoader.cs +++ b/tools/Tools.Common/Loaders/AssemblyLoader.cs @@ -15,6 +15,7 @@ using System; using System.Linq; using System.Reflection; +// TODO: Remove IfDef #if NETSTANDARD using System.Runtime.Loader; #endif @@ -66,23 +67,27 @@ public AssemblyMetadata GetReflectedAssemblyFromFile(string assemblyPath) } AssemblyMetadata result = null; +// TODO: Remove IfDef +#if NETSTANDARD try { -#if !NETSTANDARD - return new AssemblyMetadata(Assembly.ReflectionOnlyLoadFrom(assemblyPath)); -#else return new AssemblyMetadata(AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath)); } - catch(System.IO.FileLoadException ex) when (ex != null && string.Equals(ex.Message, "Assembly with same name is already loaded")) + catch(System.IO.FileLoadException ex) when (string.Equals(ex.Message, "Assembly with same name is already loaded")) { var assemblyName = AssemblyLoadContext.GetAssemblyName(assemblyPath); - var assembly = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == assemblyName?.Name).FirstOrDefault(); + var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == assemblyName?.Name); if (assembly != null) { result = new AssemblyMetadata(assembly); } -#endif } +#else + try + { + return new AssemblyMetadata(Assembly.ReflectionOnlyLoadFrom(assemblyPath)); + } +#endif catch { } diff --git a/tools/Tools.Common/Loaders/CmdletLoader.cs b/tools/Tools.Common/Loaders/CmdletLoader.cs index 68415465c07d..74e9410ba92f 100644 --- a/tools/Tools.Common/Loaders/CmdletLoader.cs +++ b/tools/Tools.Common/Loaders/CmdletLoader.cs @@ -12,7 +12,6 @@ // limitations under the License. // ---------------------------------------------------------------------------------- - using System; using System.Collections.Generic; using System.Linq; @@ -24,10 +23,11 @@ namespace Tools.Common.Loaders { -#if !NETSTANDARD - public class CmdletLoader : MarshalByRefObject -#else +// TODO: Remove IfDef +#if NETSTANDARD public class CmdletLoader +#else + public class CmdletLoader : MarshalByRefObject #endif { public static ModuleMetadata ModuleMetadata; @@ -39,7 +39,7 @@ public ModuleMetadata GetModuleMetadata(string assemblyPath, List common foreach (var commonOutputFolder in commonOutputFolders) { var assemblyName = args.Name.Substring(0, args.Name.IndexOf(",")); - var dll = Directory.GetFiles(commonOutputFolder, "*.dll").Where(f => Path.GetFileNameWithoutExtension(f) == assemblyName).FirstOrDefault(); + var dll = Directory.GetFiles(commonOutputFolder, "*.dll").FirstOrDefault(f => Path.GetFileNameWithoutExtension(f) == assemblyName); if (dll == null) { continue; @@ -61,7 +61,7 @@ public ModuleMetadata GetModuleMetadata(string assemblyPath, List common /// ModuleMetadata containing information about the cmdlets found in the given assembly. public ModuleMetadata GetModuleMetadata(string assemblyPath) { - List results = new List(); + var results = new List(); ModuleMetadata = new ModuleMetadata(); try @@ -104,7 +104,7 @@ public ModuleMetadata GetModuleMetadata(string assemblyPath) } } - List globalParameters = new List(); + var globalParameters = new List(); foreach (var parameter in parameters) { @@ -145,17 +145,14 @@ public ModuleMetadata GetModuleMetadata(string assemblyPath) foreach (var parameterSet in parameter.GetAttributes()) { - var parameterSetMetadata = cmdletMetadata.ParameterSets.FirstOrDefault(s => s.Name.Equals(parameterSet.ParameterSetName)); - - if (parameterSetMetadata == null) - { - parameterSetMetadata = new Models.ParameterSetMetadata() + var parameterSetMetadata = + cmdletMetadata.ParameterSets.FirstOrDefault(s => s.Name.Equals(parameterSet.ParameterSetName)) + ?? new Models.ParameterSetMetadata { Name = parameterSet.ParameterSetName ?? "__AllParameterSets" }; - } - Parameter param = new Parameter + var param = new Parameter { ParameterMetadata = parameterData, Mandatory = parameterSet.Mandatory, @@ -195,11 +192,9 @@ public ModuleMetadata GetModuleMetadata(string assemblyPath) } } - if (!cmdletMetadata.ParameterSets - .Where(p => p.Name.Equals(cmdletMetadata.DefaultParameterSetName, StringComparison.OrdinalIgnoreCase)) - .Any()) + if (!cmdletMetadata.ParameterSets.Any(p => p.Name.Equals(cmdletMetadata.DefaultParameterSetName, StringComparison.OrdinalIgnoreCase))) { - var defaultSet = new Models.ParameterSetMetadata() + var defaultSet = new Models.ParameterSetMetadata { Name = cmdletMetadata.DefaultParameterSetName }; diff --git a/tools/Tools.Common/Loggers/AnalysisLogger.cs b/tools/Tools.Common/Loggers/AnalysisLogger.cs index dbb452f73e23..2d9ece7e5147 100644 --- a/tools/Tools.Common/Loggers/AnalysisLogger.cs +++ b/tools/Tools.Common/Loggers/AnalysisLogger.cs @@ -27,8 +27,8 @@ namespace Tools.Common.Loggers /// public class AnalysisLogger { - string _baseDirectory; - string _exceptionsDirectory; + private readonly string _baseDirectory; + private readonly string _exceptionsDirectory; private static string _defaultLogName; private static Dictionary _logDictionary; @@ -36,15 +36,7 @@ public class AnalysisLogger private static Dictionary LogDictionary { - get - { - if(_logDictionary == null) - { - _logDictionary = new Dictionary(); - } - - return _logDictionary; - } + get { return _logDictionary ?? (_logDictionary = new Dictionary()); } } /// @@ -58,12 +50,11 @@ private static AnalysisLogger GetLogger(string loggerName) AnalysisLogger log; if (!string.IsNullOrEmpty(loggerName)) { - if (!LogDictionary.TryGetValue(loggerName, out log)) - { - _defaultLogName = loggerName; - log = new AnalysisLogger(Assembly.GetEntryAssembly().Location); - LogDictionary.Add(loggerName, log); - } + if (LogDictionary.TryGetValue(loggerName, out log)) return log; + + _defaultLogName = loggerName; + log = new AnalysisLogger(Assembly.GetEntryAssembly().Location); + LogDictionary.Add(loggerName, log); } else { @@ -86,10 +77,11 @@ public AnalysisLogger(string baseDirectory, string exceptionsDirectory) var assembly = Assembly.GetExecutingAssembly(); var assemblyType = assembly.GetType(); _defaultLogName = assembly.GetName().Name; -#if !NETSTANDARD - Log4NetLogger = log4net.LogManager.GetLogger(_defaultLogName); -#else +// TODO: Remove IfDef +#if NETSTANDARD Log4NetLogger = log4net.LogManager.GetLogger(assemblyType); +#else + Log4NetLogger = log4net.LogManager.GetLogger(_defaultLogName); #endif } @@ -100,7 +92,7 @@ public AnalysisLogger(string baseDirectory, string exceptionsDirectory) public AnalysisLogger(string baseDirectory) : this(baseDirectory, null) { } - IList _loggers = new List(); + private readonly IList _loggers = new List(); protected virtual IList Loggers { get { return _loggers; } } @@ -148,7 +140,7 @@ public virtual void WriteReport(string name, string contents) public ReportLogger GetReportLogger(string loggerFileName) { - return Loggers.Where((log) => Path.GetFileName(log.FileName).Equals(loggerFileName, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); + return Loggers.SingleOrDefault(log => Path.GetFileName(log.FileName).Equals(loggerFileName, StringComparison.OrdinalIgnoreCase)); } /// @@ -158,7 +150,7 @@ public void WriteReports() { foreach (var logger in Loggers.Where(l => l.Records.Any())) { - StringBuilder reportText = new StringBuilder(); + var reportText = new StringBuilder(); reportText.AppendLine(logger.Records.First().PrintHeaders()); foreach (var reportRecord in logger.Records) { @@ -175,7 +167,7 @@ public void CheckForIssues(int maxSeverity) foreach (var logger in Loggers.Where(l => l.Records.Any(r => r.Severity < maxSeverity))) { hasErrors = true; - StringBuilder errorText = new StringBuilder(); + var errorText = new StringBuilder(); errorText.AppendLine(logger.Records.First().PrintHeaders()); foreach (var reportRecord in logger.Records) { @@ -247,7 +239,7 @@ public void Info(string info) public void Info(string info, IEnumerable infoCollection) { - Info(info, infoCollection, (t) => t.ToString()); + Info(info, infoCollection, t => t.ToString()); } public void Info(string info, IEnumerable infoCollection, Func infoItemPrintDelegate) @@ -257,14 +249,14 @@ public void Info(string info, IEnumerable infoCollection, Func public void Info(string info, string infoFormat, IEnumerable infoCollection) { - Info(info, infoFormat, infoCollection, (t) => t.ToString()); + Info(info, infoFormat, infoCollection, t => t.ToString()); } public void Info(string info, string infoFormat, IEnumerable infoCollection, Func infoItemPrintDelegate) { Info(info); - StringBuilder sb = new StringBuilder(); - foreach (T t in infoCollection) + var sb = new StringBuilder(); + foreach (var t in infoCollection) { sb.AppendLine(string.Format(infoFormat, infoItemPrintDelegate(t))); } From 7c3bbb3a44d68a8ecd9ead5a001b377cd70e0fb6 Mon Sep 17 00:00:00 2001 From: MiYanni Date: Wed, 31 Oct 2018 16:46:01 -0700 Subject: [PATCH 13/15] Fixed net452 build issue with minor variable renames. --- .../Cmdlets/Components/ApiVersionHelper.cs | 2 +- src/Storage/Commands.Storage/Common/CmdletOperationContext.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs b/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs index 39431878e712..a5cf62d7b26d 100644 --- a/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs +++ b/src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Components/ApiVersionHelper.cs @@ -150,7 +150,7 @@ static ApiVersionCache() #if NETSTANDARD Cache = new MemoryCache(new MemoryCacheOptions()); #else - _cache = MemoryCache.Default; + Cache = MemoryCache.Default; #endif } /// diff --git a/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs b/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs index 48dac7f01842..20ada53db3f9 100644 --- a/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs +++ b/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs @@ -116,7 +116,7 @@ public static OperationContext GetStorageOperationContext(Action outputW _startedRemoteCallCounter, String.Empty, e.RequestUri.ToString()); #else string message = String.Format(Resources.StartRemoteCall, - startedRemoteCallCounter, e.Request.Method, e.Request.RequestUri.ToString()); + _startedRemoteCallCounter, e.Request.Method, e.Request.RequestUri.ToString()); #endif try From abe7123d068667ff4178bc5316f4f0fc86e09b70 Mon Sep 17 00:00:00 2001 From: MiYanni Date: Wed, 31 Oct 2018 16:59:22 -0700 Subject: [PATCH 14/15] Updated appropriate changelogs. --- src/ResourceManager/Aks/Commands.Aks/ChangeLog.md | 1 + .../AnalysisServices/Commands.AnalysisServices/ChangeLog.md | 1 + src/ResourceManager/Automation/Commands.Automation/ChangeLog.md | 1 + src/ResourceManager/AzureBatch/Commands.Batch/ChangeLog.md | 1 + src/ResourceManager/Compute/Commands.Compute/ChangeLog.md | 1 + .../ContainerInstance/Commands.ContainerInstance/ChangeLog.md | 1 + .../DataFactories/Commands.DataFactories/ChangeLog.md | 1 + .../DataLakeAnalytics/Commands.DataLakeAnalytics/ChangeLog.md | 1 + src/ResourceManager/KeyVault/Commands.KeyVault/ChangeLog.md | 1 + src/ResourceManager/Network/Commands.Network/ChangeLog.md | 1 + src/ResourceManager/Profile/Commands.Profile/ChangeLog.md | 1 + .../RecoveryServices/Commands.RecoveryServices/ChangeLog.md | 1 + src/ResourceManager/Resources/Commands.Resources/ChangeLog.md | 1 + src/ResourceManager/Sql/Commands.Sql/ChangeLog.md | 1 + src/ResourceManager/Websites/Commands.Websites/ChangeLog.md | 1 + src/Storage/Commands.Storage/ChangeLog.md | 1 + 16 files changed, 16 insertions(+) diff --git a/src/ResourceManager/Aks/Commands.Aks/ChangeLog.md b/src/ResourceManager/Aks/Commands.Aks/ChangeLog.md index aab8b580549a..4518e87919e2 100644 --- a/src/ResourceManager/Aks/Commands.Aks/ChangeLog.md +++ b/src/ResourceManager/Aks/Commands.Aks/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 0.0.6 * Update version of YamlDotNet.Signed used diff --git a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices/ChangeLog.md b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices/ChangeLog.md index b4d55aa33214..c72a3399b612 100644 --- a/src/ResourceManager/AnalysisServices/Commands.AnalysisServices/ChangeLog.md +++ b/src/ResourceManager/AnalysisServices/Commands.AnalysisServices/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 0.6.14 * Fixed issue with default resource groups not being set. diff --git a/src/ResourceManager/Automation/Commands.Automation/ChangeLog.md b/src/ResourceManager/Automation/Commands.Automation/ChangeLog.md index 69521f1e0b15..0d9839a638bd 100644 --- a/src/ResourceManager/Automation/Commands.Automation/ChangeLog.md +++ b/src/ResourceManager/Automation/Commands.Automation/ChangeLog.md @@ -19,6 +19,7 @@ --> ## Current Release * Renamed cmdlet DLL filename to Microsoft.Azure.Commands.Automation.dll +* Minor changes for upcoming AzureRM to Az transition ## Version 5.1.1 * Fixed issue with default resource groups not being set. diff --git a/src/ResourceManager/AzureBatch/Commands.Batch/ChangeLog.md b/src/ResourceManager/AzureBatch/Commands.Batch/ChangeLog.md index 4853e8a38828..ee11a1088387 100644 --- a/src/ResourceManager/AzureBatch/Commands.Batch/ChangeLog.md +++ b/src/ResourceManager/AzureBatch/Commands.Batch/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 4.1.5 * Fixed issue with default resource groups not being set. diff --git a/src/ResourceManager/Compute/Commands.Compute/ChangeLog.md b/src/ResourceManager/Compute/Commands.Compute/ChangeLog.md index 8a3a44c5d49a..0b19f31269d4 100644 --- a/src/ResourceManager/Compute/Commands.Compute/ChangeLog.md +++ b/src/ResourceManager/Compute/Commands.Compute/ChangeLog.md @@ -19,6 +19,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 5.7.2 * Add EnableUltraSSD switch parameter to New-AzureRmVMConfiig and New-AzureRmVmssConfig. diff --git a/src/ResourceManager/ContainerInstance/Commands.ContainerInstance/ChangeLog.md b/src/ResourceManager/ContainerInstance/Commands.ContainerInstance/ChangeLog.md index 185c135d18da..b0bfd94f368c 100644 --- a/src/ResourceManager/ContainerInstance/Commands.ContainerInstance/ChangeLog.md +++ b/src/ResourceManager/ContainerInstance/Commands.ContainerInstance/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 0.2.10 * Fixed issue with default resource groups not being set. diff --git a/src/ResourceManager/DataFactories/Commands.DataFactories/ChangeLog.md b/src/ResourceManager/DataFactories/Commands.DataFactories/ChangeLog.md index 07b18dda20dc..aac2bc4e9b9a 100644 --- a/src/ResourceManager/DataFactories/Commands.DataFactories/ChangeLog.md +++ b/src/ResourceManager/DataFactories/Commands.DataFactories/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 5.0.3 * Updated to the latest version of the Azure ClientRuntime. diff --git a/src/ResourceManager/DataLakeAnalytics/Commands.DataLakeAnalytics/ChangeLog.md b/src/ResourceManager/DataLakeAnalytics/Commands.DataLakeAnalytics/ChangeLog.md index 1cfc34b12414..5da2ade5823c 100644 --- a/src/ResourceManager/DataLakeAnalytics/Commands.DataLakeAnalytics/ChangeLog.md +++ b/src/ResourceManager/DataLakeAnalytics/Commands.DataLakeAnalytics/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 5.1.4 * Fixed issue with default resource groups not being set. diff --git a/src/ResourceManager/KeyVault/Commands.KeyVault/ChangeLog.md b/src/ResourceManager/KeyVault/Commands.KeyVault/ChangeLog.md index 2ec017890d93..f183b86b6087 100644 --- a/src/ResourceManager/KeyVault/Commands.KeyVault/ChangeLog.md +++ b/src/ResourceManager/KeyVault/Commands.KeyVault/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 5.2.1 * Fixed issue with default resource groups not being set. diff --git a/src/ResourceManager/Network/Commands.Network/ChangeLog.md b/src/ResourceManager/Network/Commands.Network/ChangeLog.md index 26e17e4cabc1..768b9a0c3ff2 100644 --- a/src/ResourceManager/Network/Commands.Network/ChangeLog.md +++ b/src/ResourceManager/Network/Commands.Network/ChangeLog.md @@ -25,6 +25,7 @@ - Get-AzureRMExpressRouteCrossConnectionArpTable - Get-AzureRMExpressRouteCrossConnectionRouteTable - Get-AzureRMExpressRouteCrossConnectionRouteTableSummary +* Minor changes for upcoming AzureRM to Az transition ## Version 6.9.1 * Update cmdlet Test-AzureRmNetworkWatcherConnectivity, pass the protocol value to backend. diff --git a/src/ResourceManager/Profile/Commands.Profile/ChangeLog.md b/src/ResourceManager/Profile/Commands.Profile/ChangeLog.md index d13883466e9e..491081dc36c5 100644 --- a/src/ResourceManager/Profile/Commands.Profile/ChangeLog.md +++ b/src/ResourceManager/Profile/Commands.Profile/ChangeLog.md @@ -29,6 +29,7 @@ - https://github.com/Azure/azure-powershell/issues/7462 * Fix issue where `Disconnect-AzureRmAccount` would throw if not connected - https://github.com/Azure/azure-powershell/issues/7167 +* Minor changes for upcoming AzureRM to Az transition ## Version 5.7.0 * Fix issue with Get-AzureRmSubscription in CloudShell diff --git a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/ChangeLog.md b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/ChangeLog.md index 2e0237c09629..6662cf51f10e 100644 --- a/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/ChangeLog.md +++ b/src/ResourceManager/RecoveryServices/Commands.RecoveryServices/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 4.1.8 * Fixed issue with default resource groups not being set. diff --git a/src/ResourceManager/Resources/Commands.Resources/ChangeLog.md b/src/ResourceManager/Resources/Commands.Resources/ChangeLog.md index c6f2a15cf7a5..da8b14c3656f 100644 --- a/src/ResourceManager/Resources/Commands.Resources/ChangeLog.md +++ b/src/ResourceManager/Resources/Commands.Resources/ChangeLog.md @@ -20,6 +20,7 @@ ## Current Release * Fix for https://github.com/Azure/azure-powershell/issues/7402 - Allow listing resources using the `-ResourceId` parameter for `Get-AzureRmResource` +* Minor changes for upcoming AzureRM to Az transition ## Version 6.7.0 * Fix isssue where Get-AzureRMRoleDefinition throws an unintelligible exception (when the default profile has no subscription in it and no scope is specified) by adding a meaningful exception in the scenario. Also set the default param set to `RoleDefinitionNameParameterSet`. diff --git a/src/ResourceManager/Sql/Commands.Sql/ChangeLog.md b/src/ResourceManager/Sql/Commands.Sql/ChangeLog.md index e9226a53c436..37a5d291a501 100644 --- a/src/ResourceManager/Sql/Commands.Sql/ChangeLog.md +++ b/src/ResourceManager/Sql/Commands.Sql/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 4.11.5 * Fixed issue where some backup cmdlets would not recognize the current azure subscription diff --git a/src/ResourceManager/Websites/Commands.Websites/ChangeLog.md b/src/ResourceManager/Websites/Commands.Websites/ChangeLog.md index ab3982855df7..6c27f97e61dd 100644 --- a/src/ResourceManager/Websites/Commands.Websites/ChangeLog.md +++ b/src/ResourceManager/Websites/Commands.Websites/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 5.2.0 * New Cmdlet Get-AzureRMWebAppContainerContinuousDeploymentUrl - Gets the Container Continuous Deployment Webhook URL diff --git a/src/Storage/Commands.Storage/ChangeLog.md b/src/Storage/Commands.Storage/ChangeLog.md index d3dc300e37fd..c231dba9dc0a 100644 --- a/src/Storage/Commands.Storage/ChangeLog.md +++ b/src/Storage/Commands.Storage/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Current Release +* Minor changes for upcoming AzureRM to Az transition ## Version 4.6.1 * Fix Copy Blob/File won't copy metadata when destination has metadata issue From 83ebf6b24f30ec0c30f3ba02bc067d39c7090bb4 Mon Sep 17 00:00:00 2001 From: MiYanni Date: Tue, 27 Nov 2018 12:10:02 -0800 Subject: [PATCH 15/15] Addressing PR review comments. --- .../Models/KeyVaultManagementCmdletBase.cs | 11 ++++---- .../ResourcesExtensions.cs | 25 ++++++++++--------- .../Common/AzureEndpointsCommunicator.cs | 11 ++++---- ...rabilityAssessmentEndpointsCommunicator.cs | 9 ++++--- .../AppServicePlans/SetAzureAppServicePlan.cs | 2 +- .../Common/CmdletOperationContext.cs | 18 +++++++------ 6 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/KeyVaultManagementCmdletBase.cs b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/KeyVaultManagementCmdletBase.cs index cd2a06175c32..3366a7a6417f 100644 --- a/src/ResourceManager/KeyVault/Commands.KeyVault/Models/KeyVaultManagementCmdletBase.cs +++ b/src/ResourceManager/KeyVault/Commands.KeyVault/Models/KeyVaultManagementCmdletBase.cs @@ -164,13 +164,14 @@ protected string GetResourceGroupName(string vaultName) ResourceType = KeyVaultManagementClient.VaultsResourceType }); - if (resourcesByName == null || resourcesByName.Count <= 0) return null; - - var vault = resourcesByName.FirstOrDefault(r => r.Name.Equals(vaultName, StringComparison.OrdinalIgnoreCase)); string rg = null; - if (vault != null) + if (resourcesByName != null && resourcesByName.Count > 0) { - rg = new ResourceIdentifier(vault.Id).ResourceGroupName; + var vault = resourcesByName.FirstOrDefault(r => r.Name.Equals(vaultName, StringComparison.OrdinalIgnoreCase)); + if (vault != null) + { + rg = new ResourceIdentifier(vault.Id).ResourceGroupName; + } } return rg; diff --git a/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs b/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs index 6a0bde7b098a..a4c071b7e99e 100644 --- a/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs +++ b/src/ResourceManager/Resources/Commands.Resources/Models.ResourceGroups/ResourcesExtensions.cs @@ -166,22 +166,23 @@ public static string ConstructTagsTable(Hashtable tags) public static string ConstructPermissionsTable(List permissions) { - if (permissions == null || permissions.Count <= 0) return string.Empty; - - var maxActionsLength = Math.Max("Actions".Length, permissions.Where(p => p.Actions != null).DefaultIfEmpty(EmptyPermission).Max(p => p.ActionsString.Length)); - var maxNotActionsLength = Math.Max("NotActions".Length, permissions.Where(p => p.NotActions != null).DefaultIfEmpty(EmptyPermission).Max(p => p.NotActionsString.Length)); - - var rowFormat = "{0, -" + maxActionsLength + "} {1, -" + maxNotActionsLength + "}\r\n"; var permissionsTable = new StringBuilder(); - permissionsTable.AppendLine(); - permissionsTable.AppendFormat(rowFormat, "Actions", "NotActions"); - permissionsTable.AppendFormat(rowFormat, + if (permissions != null && permissions.Count > 0) + { + var maxActionsLength = Math.Max("Actions".Length, permissions.Where(p => p.Actions != null).DefaultIfEmpty(EmptyPermission).Max(p => p.ActionsString.Length)); + var maxNotActionsLength = Math.Max("NotActions".Length, permissions.Where(p => p.NotActions != null).DefaultIfEmpty(EmptyPermission).Max(p => p.NotActionsString.Length)); + + var rowFormat = "{0, -" + maxActionsLength + "} {1, -" + maxNotActionsLength + "}\r\n"; + permissionsTable.AppendLine(); + permissionsTable.AppendFormat(rowFormat, "Actions", "NotActions"); + permissionsTable.AppendFormat(rowFormat, GeneralUtilities.GenerateSeparator(maxActionsLength, "="), GeneralUtilities.GenerateSeparator(maxNotActionsLength, "=")); - foreach (var permission in permissions) - { - permissionsTable.AppendFormat(rowFormat, permission.ActionsString, permission.NotActionsString); + foreach (var permission in permissions) + { + permissionsTable.AppendFormat(rowFormat, permission.ActionsString, permission.NotActionsString); + } } return permissionsTable.ToString(); diff --git a/src/ResourceManager/Sql/Commands.Sql/Common/AzureEndpointsCommunicator.cs b/src/ResourceManager/Sql/Commands.Sql/Common/AzureEndpointsCommunicator.cs index dad2c9657721..156f90ca2bc2 100644 --- a/src/ResourceManager/Sql/Commands.Sql/Common/AzureEndpointsCommunicator.cs +++ b/src/ResourceManager/Sql/Commands.Sql/Common/AzureEndpointsCommunicator.cs @@ -60,11 +60,12 @@ public class AzureEndpointsCommunicator public AzureEndpointsCommunicator(IAzureContext context) { Context = context; - if (context.Subscription == Subscription) return; - - Subscription = context.Subscription; - ResourcesClient = null; - StorageV2Client = null; + if (context.Subscription != Subscription) + { + Subscription = context.Subscription; + ResourcesClient = null; + StorageV2Client = null; + } } private static class StorageAccountType diff --git a/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/VulnerabilityAssessmentEndpointsCommunicator.cs b/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/VulnerabilityAssessmentEndpointsCommunicator.cs index 3c21cf4a18fa..a3da2c262536 100644 --- a/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/VulnerabilityAssessmentEndpointsCommunicator.cs +++ b/src/ResourceManager/Sql/Commands.Sql/VulnerabilityAssessment/Services/VulnerabilityAssessmentEndpointsCommunicator.cs @@ -57,10 +57,11 @@ public class VulnerabilityAssessmentEndpointsCommunicator public VulnerabilityAssessmentEndpointsCommunicator(IAzureContext context) { Context = context; - if (context.Subscription == Subscription) return; - - Subscription = context.Subscription; - SqlClient = null; + if (context.Subscription != Subscription) + { + Subscription = context.Subscription; + SqlClient = null; + } } /// diff --git a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/SetAzureAppServicePlan.cs b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/SetAzureAppServicePlan.cs index 58085e561837..901b7e7b8488 100644 --- a/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/SetAzureAppServicePlan.cs +++ b/src/ResourceManager/Websites/Commands.Websites/Cmdlets/AppServicePlans/SetAzureAppServicePlan.cs @@ -58,7 +58,7 @@ public override void ExecuteCmdlet() AppServicePlan = new PSAppServicePlan(WebsitesClient.GetAppServicePlan(ResourceGroupName, Name)); AppServicePlan.Sku.Tier = string.IsNullOrWhiteSpace(Tier) ? AppServicePlan.Sku.Tier : Tier; AppServicePlan.Sku.Capacity = NumberofWorkers > 0 ? NumberofWorkers : AppServicePlan.Sku.Capacity; - int workerSizeAsNumber; + int workerSizeAsNumber = 0; int.TryParse(Regex.Match(AppServicePlan.Sku.Name, @"\d+").Value, out workerSizeAsNumber); AppServicePlan.Sku.Name = string.IsNullOrWhiteSpace(WorkerSize) ? CmdletHelpers.GetSkuName(AppServicePlan.Sku.Tier, workerSizeAsNumber) : CmdletHelpers.GetSkuName(AppServicePlan.Sku.Tier, WorkerSize); AppServicePlan.PerSiteScaling = PerSiteScaling; diff --git a/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs b/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs index 20ada53db3f9..511200102591 100644 --- a/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs +++ b/src/Storage/Commands.Storage/Common/CmdletOperationContext.cs @@ -68,15 +68,17 @@ private CmdletOperationContext() { } /// public static void Init() { - if (_inited) return; - - lock (SyncRoot) + if (!_inited) { - if (_inited) return; - - StartTime = DateTime.Now; - ClientRequestId = GenClientRequestID(); - _inited = true; + lock (SyncRoot) + { + if (!_inited) + { + StartTime = DateTime.Now; + ClientRequestId = GenClientRequestID(); + _inited = true; + } + } } }