aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Azure/Tango.AzureUtils/Deployment/DeploymentManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio/Azure/Tango.AzureUtils/Deployment/DeploymentManager.cs')
-rw-r--r--Software/Visual_Studio/Azure/Tango.AzureUtils/Deployment/DeploymentManager.cs484
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
}
}