/Bowlus/Source-Notify-Poll/Bowlus.AppHost/AppHostManager.cs
C# | 269 lines | 198 code | 31 blank | 40 comment | 32 complexity | 2fc07b659211133f62f4a66366e44059 MD5 | raw file
- namespace Bowlus.AppHost
- {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Security.AccessControl;
- using System.Threading;
-
- using Bowlus.Common;
-
- using Microsoft.Web.Administration;
- using Microsoft.WindowsAzure.ServiceRuntime;
-
- // Maintain the List of Applications Deployed to this Instance
- public class IISAppInfo
- {
- // i.e. 'test1.bowlus.net'
- public string HostHeader { get; set; }
-
- public string CurrentPath { get; set; }
-
- public DateTime Updated { get; set; }
- }
-
- // AppHost Manager
- public class AppHostManager
- {
- private readonly StorageAppRepository appRepository;
-
- // local root path for all sites
- private string rootSitePath;
-
- private string appCachePath;
-
- // provision a site with Staging Status == "provisioning"
- private bool provisionSite(TenantDeployment td)
- {
- // Unzip the site to the host folder
- var sitePath = Path.Combine(rootSitePath, td.HostName, DateTime.UtcNow.Ticks.ToString());
- appRepository.WriteAppTo(td.Application, sitePath);
-
- // provision in IIS config
- var siteName = RoleEnvironment.CurrentRoleInstance.Id + "_" + td.HostName;
- //<sites>
- // <site name="Bowlus.AppHost_IN_0_test1.thecloudsamurai.com" id="1">
- // <application path="/" applicationPool="test1">
- // <virtualDirectory path="/" physicalPath="C:\Resources\directory\37950156f46746a48bf597b89b5e832d.Bowlus.AppHost.Sites\test1.thecloudsamurai.com\634690683032291523\Site" />
- // </application>
- // <bindings>
- // <binding protocol="http" bindingInformation="10.115.48.42:8080:test1.thecloudsamurai.com" />
- // </bindings>
- // </site>
- //</sites>
- var iisApp = new IISAppInfo();
- iisApp.HostHeader = td.HostName;
- // contents in zip file is under 'Site' directory
- iisApp.CurrentPath = Path.Combine(sitePath, "Site");
- iisApp.Updated = new FileInfo(sitePath).LastWriteTimeUtc;
-
- // try 3 times
- for (int i = 0; ; i++)
- {
- try
- {
- // add site to IIS config
- lock (BowlusHelper.ServerManagerLock)
- {
- var sm = BowlusHelper.ServerManager;
- if (sm.Sites.Count(s => s.Name == siteName) > 0)
- {
- Trace.TraceWarning("Site already exists in IIS, however we still update and re-provision it." + siteName);
- var site = sm.Sites.SingleOrDefault(s => s.Name == (siteName));
- var hostHeaderParts = iisApp.HostHeader.Split('.');
- var appPoolName = hostHeaderParts.Length == 3 ? hostHeaderParts[0] : iisApp.HostHeader;
- site.Applications.First().ApplicationPoolName = GetAppPool(sm, appPoolName).Name;
- }
- else
- {
- var newSite = sm.Sites.Add(siteName, "http",
- RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Http"].IPEndpoint + ":" + iisApp.HostHeader, iisApp.CurrentPath);
- var hostHeaderParts = iisApp.HostHeader.Split('.');
- var appPoolName = hostHeaderParts.Length == 3 ? hostHeaderParts[0] : iisApp.HostHeader;
- newSite.Applications.First().ApplicationPoolName = GetAppPool(sm, appPoolName).Name;
- }
- sm.CommitChanges();
- }
- break;
- }
- catch (Exception ex)
- {
- Trace.TraceError(ex.Message + '\n' + ex.StackTrace);
- if (i == 3) throw;
- Thread.Sleep(3000);
- }
- }
-
- // update td status
- td.StagingId = RoleEnvironment.CurrentRoleInstance.Id;
- td.SStatus = TenantDeployment.S_PROVISIONED;
- TentantDepTableContext.Instance.UpdateTD(td);
-
- Trace.TraceInformation("Site provisioned: " + siteName);
-
- return true;
- }
-
- // deprovision a site with Staging Status == "deprovisioning"
- private bool deprovisionSite(TenantDeployment td)
- {
- // delete from IIS config
- var siteName = RoleEnvironment.CurrentRoleInstance.Id + "_" + td.HostName;
- if (BowlusHelper.ServerManager.Sites.Count(s => s.Name == siteName) == 0)
- {
- Trace.TraceWarning("Cannot deprovision from IIS a site does not exist. " + siteName);
- }
- else
- {
- // try 3 times
- for (int i = 0; ; i++)
- {
- try
- {
- // real logic, to be tried
- lock (BowlusHelper.ServerManagerLock)
- {
- var sm = BowlusHelper.ServerManager;
- var site = sm.Sites.SingleOrDefault(s => s.Name == (siteName));
- var appPoolName = site.Applications[0].ApplicationPoolName;
- var appPool = sm.ApplicationPools.SingleOrDefault(ap => ap.Name == appPoolName);
- sm.ApplicationPools.Remove(appPool);
- sm.Sites.Remove(site);
- sm.CommitChanges();
- }
- break;
- }
- catch (Exception ex)
- {
- Trace.TraceError(ex.Message + '\n' + ex.StackTrace);
- if (i == 3) throw;
- Thread.Sleep(3000);
- }
- }
- }
-
- // delete from IIS server file directory
- var directory = Directory.EnumerateDirectories(rootSitePath).SingleOrDefault(d => d.Contains(td.HostName));
- Directory.Delete(directory, true);
-
- // delete from tenant deployment table
- TentantDepTableContext.Instance.DeleteTD(td);
-
- Trace.TraceInformation("Site deprovisioned: " + siteName);
- return true;
- }
-
- // sync can be safely called any times and at anytime.
- public int Sync()
- {
- var ctx = TentantDepTableContext.Instance;
- string localIpv4Address = BowlusHelper.GetHostIpAddress();
- int changes = 0;
-
- var q = (from td in ctx.TDQueryable
- where td.SStatus == TenantDeployment.S_PROVISIONING && td.Staging == localIpv4Address
- select td).ToList<TenantDeployment>();
- foreach (var td in q)
- {
- try
- {
- if (provisionSite(td)) changes++;
- }
- catch (Exception ex)
- {
- Trace.TraceError("Provision fail: "+ td.HostName + ", " + ex.Message);
- if (RoleEnvironment.IsEmulated)
- throw;
- }
- }
-
- q = (from td in ctx.TDQueryable
- // where td.SStatus == TenantDeployment.S_DEPROVISIONING && td.Staging == localIpv4Address
- where td.SStatus == TenantDeployment.S_DEPROVISIONING && td.InstanceId == RoleEnvironment.CurrentRoleInstance.Id
- select td).ToList<TenantDeployment>();
- foreach (var td in q)
- {
- try
- {
- if (deprovisionSite(td)) changes++;
- }
- catch (Exception ex)
- {
- Trace.TraceError("Deprovision fail: " + td.HostName + ", " + ex.Message);
- if (RoleEnvironment.IsEmulated)
- throw;
- }
- }
-
- return changes;
- }
-
- // get, or create if not exist, application pool for the site
- private static ApplicationPool GetAppPool(ServerManager sm, string siteName)
- {
- var appPool = sm.ApplicationPools.SingleOrDefault(p => p.Name == siteName);
- if (appPool == null)
- {
- appPool = sm.ApplicationPools.Add(siteName);
- appPool.ManagedRuntimeVersion = "v4.0";
- appPool.ProcessModel.LoadUserProfile = false;
- }
- return appPool;
- }
-
- // Configure the directory security.
- private static void ConfigureDirectorySecurity(string rootPath, string path)
- {
- var sec = Directory.GetAccessControl(rootPath);
- sec.AddAccessRule(new FileSystemAccessRule("Everyone", FileSystemRights.FullControl, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow));
- Directory.SetAccessControl(path, sec);
- }
-
- // set appRepository and serverManager
- public AppHostManager(StorageAppRepository repository)
- {
- appRepository = repository;
- }
-
- // 1) init sitesPath and appCachePath
- // 2) provision active sites
- // init will be called at a later time than AppHostManager constructor, when the VM is ready.
- public void Initialize()
- {
- Trace.TraceInformation("Initialize AppHostManager");
-
- try
- {
- // Initialize Local Resources
- this.rootSitePath = RoleEnvironment.GetLocalResource("Sites").RootPath.TrimEnd('\\');
- this.appCachePath = RoleEnvironment.GetLocalResource("AppCache").RootPath.TrimEnd('\\');
-
- // Configure Security on our directories
- ConfigureDirectorySecurity(this.rootSitePath, this.rootSitePath);
- ConfigureDirectorySecurity(this.rootSitePath, this.appCachePath);
-
- // try to provision active site.
- // this will only happen when the VM starts, while the TenantDeployment table 'Active' sites
- // are not in VM. Because the deployment table persists while VMs don't.
-
- var ctx = TentantDepTableContext.Instance;
- var q = (from td in ctx.TDQueryable
- where td.Status == TenantDeployment.S_ACTIVE && td.InstanceId == RoleEnvironment.CurrentRoleInstance.Id
- select td).ToList<TenantDeployment>();
- foreach (var td in q)
- {
- provisionSite(td);
- }
-
- Sync();
- }
- catch (Exception ex)
- {
- Trace.TraceError(ex.Message + '\n' + ex.StackTrace);
- throw;
- }
- }
- }
- }