diff options
Diffstat (limited to 'Software/Visual_Studio/Azure/Tango.AzureUtils/Deployment/DeploymentManager.cs')
| -rw-r--r-- | Software/Visual_Studio/Azure/Tango.AzureUtils/Deployment/DeploymentManager.cs | 484 |
1 files changed, 451 insertions, 33 deletions
diff --git a/Software/Visual_Studio/Azure/Tango.AzureUtils/Deployment/DeploymentManager.cs b/Software/Visual_Studio/Azure/Tango.AzureUtils/Deployment/DeploymentManager.cs index e9ae6e97b..8a3b6ad7b 100644 --- a/Software/Visual_Studio/Azure/Tango.AzureUtils/Deployment/DeploymentManager.cs +++ b/Software/Visual_Studio/Azure/Tango.AzureUtils/Deployment/DeploymentManager.cs @@ -1,4 +1,5 @@ -using Microsoft.Azure.Management.AppService.Fluent; +using FluentFTP; +using Microsoft.Azure.Management.AppService.Fluent; using Microsoft.Azure.Management.Fluent; using Microsoft.Azure.Management.ResourceManager.Fluent; using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication; @@ -6,27 +7,70 @@ using Microsoft.Azure.Storage; using Microsoft.Azure.Storage.Blob; using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Data.Entity; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml; +using System.Xml.Linq; +using Tango.BL; +using Tango.BL.Entities; +using Tango.Core; +using Tango.Core.Helpers; namespace Tango.AzureUtils.Deployment { - public class DeploymentManager + public class DeploymentManager : ExtendedObject { private AzureUtilsCredentials _credentials; private IAzure _azure; + private IProgress<FtpProgress> _ftpDownloadProgress; + private IProgress<FtpProgress> _ftpUploadProgress; + + //TODO: Embedded TFP injection to current package! + + #region Events + + public event EventHandler<DeploymentProgressEventArgs> DeploymentProgress; + + #endregion + + #region Properties + + private UpgradeConfiguration _upgradeConfiguration; + public UpgradeConfiguration UpgradeConfiguration + { + get { return _upgradeConfiguration; } + set { _upgradeConfiguration = value; RaisePropertyChangedAuto(); } + } + + #endregion + + #region Constructors public DeploymentManager(AzureUtilsCredentials credentials) { + UpgradeConfiguration = new UpgradeConfiguration(); + _credentials = credentials; + + _ftpDownloadProgress = new Progress<FtpProgress>((p) => + { + OnProgress(DeploymentStage.DownloadingFTP, $"Downloading {p.RemotePath}...", p.Progress, 100, false); + }); + + _ftpUploadProgress = new Progress<FtpProgress>((p) => + { + OnProgress(DeploymentStage.UploadingFTP, $"Uploading {p.LocalPath}...", p.Progress, 100, false); + }); } - private static string app_id = "be33437c-5052-449f-ab9d-a88d008eae24"; - private static string client_secret = "bf67fb6f-4d06-4893-988c-6b347aff23d6"; - private static string tenant_id = "2ebd63a5-bc2f-41dc-9066-4409ed5e5dd4"; - private static string subscription_id = "10c8aa60-3b15-4e0d-b412-6aeef90e5e91"; + #endregion + + #region Authenticate private IAzure GetOrCreateAzure() { @@ -44,56 +88,430 @@ namespace Tango.AzureUtils.Deployment return _azure; } - public List<IWebApp> GetAllWebApps() + #endregion + + #region Helpers + + public async Task<List<IWebApp>> GetAllWebAppsAsync() + { + return (await GetOrCreateAzure().WebApps.ListAsync()).ToList(); + } + + #endregion + + #region Init + + public void Init() + { + GetOrCreateAzure(); + } + + #endregion + + #region Full Upgrade + + public async Task PerformFullUpgrade(IWebAppBase sourceApp, IWebAppBase targetApp) + { + await ValidateUpgrade(sourceApp, targetApp); + + await UpgradeStorage(sourceApp, targetApp); + await UpgradeVersions(sourceApp, targetApp); + + if (UpgradeConfiguration.UpgradeMachineService) + { + await UpgradeMachineService(sourceApp, targetApp); + } + } + + #endregion + + #region SQLExaminer + + public async Task OpenSQLExaminerSchema(IWebAppBase sourceApp, IWebAppBase targetApp) + { + String projectFile = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "Deployment", "GENERAL_ENV_UPGRADE.seproj"); + + using (Stream stream = GetFileStream(projectFile)) + { + XElement projectXml = XElement.Load(stream); + var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync(); + var targetSettings = await targetApp.GetMachineServiceSettingsAsync(); + ApplyDatabaseSettingsToProjectXml(projectXml, sourceSettings, targetSettings); + + var tempFile = TemporaryManager.CreateImaginaryFile(".seproj"); + tempFile.Persist = true; + + File.WriteAllText(tempFile, projectXml.ToString()); + + Process.Start(tempFile); + } + } + + public async Task OpenSQLExaminerData(IWebAppBase sourceApp, IWebAppBase targetApp) + { + String projectFile = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), "Deployment", "GENERAL_ENV_UPGRADE.sdeproj"); + + using (Stream stream = GetFileStream(projectFile)) + { + XElement projectXml = XElement.Load(stream); + var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync(); + var targetSettings = await targetApp.GetMachineServiceSettingsAsync(); + ApplyDatabaseSettingsToProjectXml(projectXml, sourceSettings, targetSettings); + + var tempFile = TemporaryManager.CreateImaginaryFile(".sdeproj"); + tempFile.Persist = true; + + File.WriteAllText(tempFile, projectXml.ToString()); + + Process.Start(tempFile); + } + } + + private Stream GetFileStream(String projectFile) + { + byte[] projectBytes = File.ReadAllBytes(projectFile); + MemoryStream stream = new MemoryStream(projectBytes); + return stream; + } + + private void ApplyDatabaseSettingsToProjectXml(XElement projectXml, MachineServiceSettings sourceSettings, MachineServiceSettings targetSettings) + { + var sourceElement = projectXml.Elements().SelectMany(x => x.Descendants()).SingleOrDefault(x => x.Name == "Source" && x.Attributes().SingleOrDefault(y => y.Name == "id").Value == "1"); + var targetElement = projectXml.Elements().SelectMany(x => x.Descendants()).SingleOrDefault(x => x.Name == "Source" && x.Attributes().SingleOrDefault(y => y.Name == "id").Value == "2"); + + ApplyDatabaseSettingsToSourceElement(sourceElement, sourceSettings); + ApplyDatabaseSettingsToSourceElement(targetElement, targetSettings); + } + + private void ApplyDatabaseSettingsToSourceElement(XElement sourceElement, MachineServiceSettings settings) + { + sourceElement.Element("ServerName").SetValue(settings.DB_ADDRESS); + sourceElement.Element("Database").SetValue(settings.DB_CATALOG); + sourceElement.Element("Login").SetValue(settings.DB_USER_NAME); + sourceElement.Element("Password").SetValue(settings.DB_PASSWORD); + } + + #endregion + + #region FTP + + private async Task<List<FtpResult>> DownloadWebAppFiles(IWebAppBase app, String targetFolder) + { + var profile = await app.GetPublishingProfileAsync(); + + using (var ftp = new FtpClient(profile.FtpUrl, profile.FtpUsername, profile.FtpPassword)) + { + var downloadResults = await ftp.DownloadDirectoryAsync(targetFolder, "/site/wwwroot", progress: _ftpDownloadProgress); + + foreach (var downloadResult in downloadResults) + { + if (downloadResult.IsFailed) + { + throw downloadResult.Exception; + } + } + + return downloadResults; + } + } + + private async Task<List<FtpResult>> UploadWebAppFiles(IWebAppBase app, String sourceFolder) + { + var profile = await app.GetPublishingProfileAsync(); + + using (var ftp = new FtpClient(profile.FtpUrl, profile.FtpUsername, profile.FtpPassword)) + { + var uploadResults = await ftp.UploadDirectoryAsync(sourceFolder, "/site/wwwroot", existsMode: FtpRemoteExists.Overwrite, progress: _ftpUploadProgress); + + foreach (var uploadResult in uploadResults) + { + if (uploadResult.IsFailed) + { + throw uploadResult.Exception; + } + } + + return uploadResults; + } + } + + #endregion + + #region Machine Service + + public async Task UpgradeMachineService(IWebAppBase sourceApp, IWebAppBase targetApp) + { + var webAppFilesTempFolder = TemporaryManager.CreateFolder(); + var downloadResults = await DownloadWebAppFiles(sourceApp, webAppFilesTempFolder); + var uploadResults = await UploadWebAppFiles(targetApp, webAppFilesTempFolder + "\\wwwroot"); + } + + #endregion + + #region Applications Versions & Storage Blobs + + public async Task<MachineStudioVersion> GetLatestMachineStudioVersion(IWebAppBase app) + { + MachineServiceSettings settings = null; + + try + { + settings = await app.GetMachineServiceSettingsAsync(); + } + catch (Exception ex) + { + throw new ArgumentException("Could not fetch machine service settings. Please check that all settings are available."); + } + + try + { + DataSource dataSource = settings.ToDataSource(); + + using (var db = ObservablesContext.CreateDefault(dataSource)) + { + var versions = await db.MachineStudioVersions.ToListAsync(); + var latest_machine_version = versions.OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault(); + return latest_machine_version; + } + } + catch (Exception ex) + { + throw new InvalidDataException("Could not retrieve latest Machine Studio version from database.", ex); + } + } + + public async Task<TangoVersion> GetLatestPPCVersion(IWebAppBase app) { - return GetOrCreateAzure().WebApps.List().ToList(); + MachineServiceSettings settings = null; + + try + { + settings = await app.GetMachineServiceSettingsAsync(); + } + catch (Exception ex) + { + throw new ArgumentException("Could not fetch machine service settings. Please check that all settings are available.", ex); + } + + try + { + DataSource dataSource = settings.ToDataSource(); + + using (var db = ObservablesContext.CreateDefault(dataSource)) + { + var versions = await db.TangoVersions.ToListAsync(); + var latest_machine_version = versions.OrderByDescending(x => Version.Parse(x.Version)).FirstOrDefault(); + return latest_machine_version; + } + } + catch (Exception ex) + { + throw new InvalidDataException("Could not retrieve latest PPC version from database.", ex); + } } - public void UpdateSlot(IDeploymentSlot slot) + public async Task UpgradeStorage(IWebAppBase sourceApp, IWebAppBase targetApp) { - + await ValidateUpgrade(sourceApp, targetApp); + + var latestMachineStudioVersion = await GetLatestMachineStudioVersion(sourceApp); + var latestPPCVersion = await GetLatestPPCVersion(sourceApp); + + var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync(); + var targetSettings = await targetApp.GetMachineServiceSettingsAsync(); + + var sourceAccount = CloudStorageAccount.Parse(sourceSettings.STORAGE_ACCOUNT); + var sourceClient = sourceAccount.CreateCloudBlobClient(); + + var targetAccount = CloudStorageAccount.Parse(targetSettings.STORAGE_ACCOUNT); + var targetClient = targetAccount.CreateCloudBlobClient(); + + var sourceMachineStudioContainer = sourceClient.GetContainerReference(sourceSettings.MACHINE_STUDIO_VERSIONS_CONTAINER); + var targetMachineStudioContainer = targetClient.GetContainerReference(targetSettings.MACHINE_STUDIO_VERSIONS_CONTAINER); + + var sourcePPCContainer = sourceClient.GetContainerReference(sourceSettings.TANGO_VERSIONS_CONTAINER); + var targetPPCContainer = targetClient.GetContainerReference(targetSettings.TANGO_VERSIONS_CONTAINER); + + var sourceMachineStudioBlob = sourceMachineStudioContainer.GetBlockBlobReference(latestMachineStudioVersion.BlobName); + var sourceMachineStudioInstallerBlob = sourceMachineStudioContainer.GetBlockBlobReference(latestMachineStudioVersion.InstallerBlobName); + + var targetMachineStudioBlob = CreateEmptyBlob(targetMachineStudioContainer, sourceMachineStudioBlob.Name); + var targetMachineStudioInstallerBlob = CreateEmptyBlob(targetMachineStudioContainer, sourceMachineStudioInstallerBlob.Name); + + await Task.Factory.StartNew(() => + { + targetMachineStudioBlob.StartCopy(sourceMachineStudioBlob); + targetMachineStudioInstallerBlob.StartCopy(sourceMachineStudioInstallerBlob); + }); } - public void Deploy() + public async Task UpgradeVersions(IWebAppBase sourceApp, IWebAppBase targetApp) { - var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal( - app_id, - client_secret, - tenant_id, - AzureEnvironment.AzureGlobalCloud); + await ValidateUpgrade(sourceApp, targetApp); + + if (UpgradeConfiguration.UpgradeMachineStudio) + { + await UpgradeMachineStudioVersion(sourceApp, targetApp); + } - var azure = Azure.Authenticate(credentials).WithSubscription(subscription_id); - var webApps = azure.WebApps.List(); + if (UpgradeConfiguration.UpgradePPC) + { + await UpgradePPCVersion(sourceApp, targetApp); + } + } - var machineService = webApps.SingleOrDefault(x => x.Name == "MachineService"); - var devSlot = machineService.DeploymentSlots.GetByName("MachineService-DEV"); - var devProfile = devSlot.GetPublishingProfile(); + private async Task UpgradeMachineStudioVersion(IWebAppBase sourceApp, IWebAppBase targetApp) + { + var latestMachineStudioVersion = await GetLatestMachineStudioVersion(sourceApp); - String ftpAddress = devProfile.FtpUrl; - String ftpUser = devProfile.FtpUsername; - String ftpPassword = devProfile.FtpPassword; + var targetDataSource = (await targetApp.GetMachineServiceSettingsAsync()).ToDataSource(); - foreach (var ds in machineService.DeploymentSlots.List()) + using (var db = ObservablesContext.CreateDefault(targetDataSource)) { - Console.WriteLine(ds.Name); + db.MachineStudioVersions.Add(latestMachineStudioVersion); + await db.SaveChangesAsync(); } + } - CloudStorageAccount sourceAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=tangostorage;AccountKey=S4z/D+Yg6mwMis+bs/VpcDLA9yE1iZaYq23shQlRIi2KmM9E7JY8zdZjeAPOPdG3gONHoNDEpsgH6D4cqQ/bsA==;EndpointSuffix=core.windows.net"); - CloudBlobClient sourceClient = sourceAccount.CreateCloudBlobClient(); + private async Task UpgradePPCVersion(IWebAppBase sourceApp, IWebAppBase targetApp) + { + var latestPPCVersion = await GetLatestPPCVersion(sourceApp); - var sourceContainer = sourceClient.GetContainerReference("machine-studio-versions-test"); - var sourceBlob = sourceContainer.GetBlockBlobReference("Machine Studio v4.0.34.0.zip"); + var targetDataSource = (await targetApp.GetMachineServiceSettingsAsync()).ToDataSource(); - var targetContainer = sourceClient.GetContainerReference("machine-studio-versions-dev"); + using (var db = ObservablesContext.CreateDefault(targetDataSource)) + { + db.TangoVersions.Add(latestPPCVersion); + await db.SaveChangesAsync(); + } + } - CloudBlockBlob targetBlob = targetContainer.GetBlockBlobReference(sourceBlob.Name); + private CloudBlockBlob CreateEmptyBlob(CloudBlobContainer container, String name) + { + CloudBlockBlob targetBlob = container.GetBlockBlobReference(name); using (MemoryStream ms = new MemoryStream()) { targetBlob.UploadFromStream(ms);//Empty memory stream. Will create an empty blob. } - targetBlob.StartCopy(sourceBlob); + return targetBlob; + } + + #endregion + + #region Validation + + public async Task ValidateUpgrade(IWebAppBase sourceApp, IWebAppBase targetApp) + { + if (UpgradeConfiguration.UpgradeMachineStudio) + { + await ValidateMachineStudioUpgrade(sourceApp, targetApp); + } + + if (UpgradeConfiguration.UpgradePPC) + { + await ValidatePPCUpgrade(sourceApp, targetApp); + } + } + + private async Task ValidateMachineStudioUpgrade(IWebAppBase sourceApp, IWebAppBase targetApp) + { + var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync(); + var targetSettings = await targetApp.GetMachineServiceSettingsAsync(); + + var latestSourceMachineStudioVersion = await GetLatestMachineStudioVersion(sourceApp); + + //Check if there is any source machine studio version. + if (latestSourceMachineStudioVersion == null) + { + throw new ValidationException("Could not locate a Machine Studio version entry on the source database."); + } + + var latestTargetMachineStudioVersion = await GetLatestMachineStudioVersion(targetApp); + + //Check target latest machine studio version is older if there is any. + if (latestTargetMachineStudioVersion != null && Version.Parse(latestSourceMachineStudioVersion.Version) <= Version.Parse(latestTargetMachineStudioVersion.Version)) + { + throw new ValidationException($"Machine Studio source version is '{latestSourceMachineStudioVersion.Version}' while target version is '{latestTargetMachineStudioVersion.Version}'."); + } + + var targetAccount = CloudStorageAccount.Parse(targetSettings.STORAGE_ACCOUNT); + var targetClient = targetAccount.CreateCloudBlobClient(); + + var targetMachineStudioContainer = targetClient.GetContainerReference(targetSettings.MACHINE_STUDIO_VERSIONS_CONTAINER); + + //Check machine studio binaries blob not exists on the target. + var targetMachineStudioBlob = targetMachineStudioContainer.GetBlockBlobReference(latestSourceMachineStudioVersion.BlobName); + if (await targetMachineStudioBlob.ExistsAsync()) + { + throw new ValidationException($"Machine Studio Block blob '{latestSourceMachineStudioVersion.BlobName}' already exists on the target storage."); + } + + //Check machine studio installer blob not exists on the target. + var targetMachineStudioInstallerBlob = targetMachineStudioContainer.GetBlockBlobReference(latestSourceMachineStudioVersion.InstallerBlobName); + if (await targetMachineStudioInstallerBlob.ExistsAsync()) + { + throw new ValidationException($"Machine Studio Block blob '{latestSourceMachineStudioVersion.InstallerBlobName}' already exists on the target storage."); + } + } + + private async Task ValidatePPCUpgrade(IWebAppBase sourceApp, IWebAppBase targetApp) + { + var sourceSettings = await sourceApp.GetMachineServiceSettingsAsync(); + var targetSettings = await targetApp.GetMachineServiceSettingsAsync(); + + var latestSourcePPCVersion = await GetLatestPPCVersion(sourceApp); + + //Check if there is any source PPC version. + if (latestSourcePPCVersion == null) + { + throw new ValidationException("Could not locate a PPC version entry on the source database."); + } + + var latestTargetPPCVersion = await GetLatestPPCVersion(targetApp); + + //Check target latest PPC version is older if there is any. + if (latestTargetPPCVersion != null && Version.Parse(latestSourcePPCVersion.Version) <= Version.Parse(latestTargetPPCVersion.Version)) + { + throw new ValidationException($"PPC source version is '{latestSourcePPCVersion.Version}' while target version is '{latestTargetPPCVersion.Version}'."); + } + + var targetAccount = CloudStorageAccount.Parse(targetSettings.STORAGE_ACCOUNT); + var targetClient = targetAccount.CreateCloudBlobClient(); + + var targetPPCContainer = targetClient.GetContainerReference(targetSettings.TANGO_VERSIONS_CONTAINER); + + //Check PPC binaries blob not exists on the target. + var targetPPCBlob = targetPPCContainer.GetBlockBlobReference(latestSourcePPCVersion.BlobName); + if (await targetPPCBlob.ExistsAsync()) + { + throw new ValidationException($"PPC Block blob '{latestSourcePPCVersion.BlobName}' already exists on the target storage."); + } + + //Check PPC installer blob not exists on the target. + var targetPPCInstallerBlob = targetPPCContainer.GetBlockBlobReference(latestSourcePPCVersion.InstallerBlobName); + if (await targetPPCInstallerBlob.ExistsAsync()) + { + throw new ValidationException($"PPC Block blob '{latestSourcePPCVersion.InstallerBlobName}' already exists on the target storage."); + } + } + + #endregion + + #region Virtual Methods + + protected virtual void OnProgress(DeploymentStage stage, String message = null, double progress = 0, double maximum = 100, bool indeterminate = true) + { + DeploymentProgress?.Invoke(this, new DeploymentProgressEventArgs() + { + Stage = stage, + Message = message, + Progress = progress, + Maximum = maximum, + IsIndeterminate = indeterminate, + }); } + #endregion } } |
