/Accelerator/Application.cs
C# | 1199 lines | 1020 code | 55 blank | 124 comment | 51 complexity | 903bc3942062232840240c2bf3fe715f MD5 | raw file
Possible License(s): LGPL-2.0
Large files files are truncated, but you can click here to view the full file
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text.RegularExpressions;
- using System.Threading;
- using System.Xml.Linq;
- using System.Xml.XPath;
- using Microsoft.Synchronization;
- using Microsoft.Web.Administration;
- using Microsoft.WindowsAzure.Accelerator.Diagnostics;
- using Microsoft.WindowsAzure.Accelerator.Synchronization;
- using Microsoft.WindowsAzure.ServiceRuntime;
- using Microsoft.WindowsAzure.StorageClient;
- using Microsoft.WindowsAzure.Diagnostics;
- using Timer = System.Threading.Timer;
- using Path = System.IO.Path;
- namespace Microsoft.WindowsAzure.Accelerator
- {
- /// <summary>
- /// Manages the configuration and execution of an application and its dependencies.
- /// </summary>
- public class Application
- {
-
- #region | PROPERTIES
-
- public String Name { get { return XAppConfig.GetAttribute("name"); } }
- public String Version { get { return XAppConfig.GetAttribute("version"); } }
- public List<Application> ChildApplications { get; private set; }
- public List<SyncManager> BlobSyncInstances { get; private set; }
- public Dictionary<String, Process> Processes { get; private set; }
- public Dictionary<String, LocalResource> LocalResources { get; private set; }
- public Dictionary<String, CloudStorageAccount> Accounts { get; private set; }
- public Dictionary<String, Variable> Variables { get { return _variables; } }
- public Dictionary<String, Microsoft.Web.Administration.Site> Sites { get { return _sites; } }
-
- private XElement XAppConfig { get; set; }
- private XElement Config { get { return XAppConfig.Element("configuration"); } }
- private XElement Dependencies { get { return XAppConfig.Element("dependencies"); } }
- private IEnumerable<XElement> Params { get { return XAppConfig.Descendants("param"); } }
- private IEnumerable<XElement> Vars { get { return XAppConfig.Descendants("variable"); } }
- private readonly Dictionary<String, Variable> _variables = new Dictionary<String, Variable>();
- private readonly Dictionary<String, Site> _sites = new Dictionary<String, Site>();
-
-
- #endregion
- #region | CONSTRUCTOR
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Application"/> class.
- /// </summary>
- /// <remarks>
- /// Variables / Params:
- ///
- /// Variables overwrite existing keys (whereas parameters are inherited; and do not). This allows parent
- /// applications the ability to override parameter values in constituent child applications.
- /// For example, an application may have IIS and PHP as dependant child applications. The params pointing
- /// to locations and paths are specific to the application. The application can override any param
- /// declared in IIS or PHP by simply declaring its own param of the same name/key.
- ///
- /// Processing Order:
- ///
- /// Params, variables, and process tear-down are the only 3 occasions where the parent applications goes first.
- /// In all other instances of provisioning resource, configuration, and application start orchestration; it is
- /// the child applications who perform their actions prior to the parent applications.
- /// </remarks>
- /// <param name="applicationConfigurationSection">The application configuration xml.</param>
- public Application(XElement applicationConfigurationSection)
- {
- XAppConfig = applicationConfigurationSection;
- LogLevel.Information.Trace(Name, "Initialization : Starting...");
- LogLevel.Verbose.TraceContent(Name, XAppConfig.ToString(), "Initialization : Definition :");
-
- LocalResources = new Dictionary<String, LocalResource>();
- Accounts = new Dictionary<String, CloudStorageAccount>();
- Processes = new Dictionary<String, Process>();
- BlobSyncInstances = new List<SyncManager>();
- ChildApplications = new List<Application>();
-
- //i|
- //i| Process paramerters.
- //i|
- LogLevel.Information.Trace(Name, "Initialization : Parameters : Loading.");
- foreach ( XElement parameter in Params )
- {
- //i|
- //i| Parameters are disregarded if they have already been set by a loaded application.
- //i|
- if (parameter.IsEnabled()) ProcessVariableElement(parameter, true);
- }
- LogLevel.Information.Trace(Name, "Initialization : Parameters : Loaded.");
-
- //i|
- //i| Process variables.
- //i|
- LogLevel.Information.Trace(Name, "Initialization : Variables : Loading...");
- foreach ( XElement variable in Vars )
- {
- //i|
- //i| Variables overwrite existing keys (whereas parameters are inherited; and do not).
- //i|
- if (variable.IsEnabled()) ProcessVariableElement(variable, false);
- }
- LogLevel.Information.Trace(Name, "Initialization : Variables : Loaded.");
-
- //i|
- //i| Load any child applications required by this application.
- //i|
- foreach ( var a in XAppConfig.XPathSelectElements("dependencies/applications/application").ToList())
- {
- if ( a.IsEnabled() )
- {
- XElement cs = ServiceManager.GetApplicationConfigurationSection(XAppConfig.Parent.Document, a.GetAttribute("name"), a.GetAttribute("version"));
- if (!ServiceManager.IsExistingApplication(cs.GetAttribute("name"), cs.GetAttribute("version")))
- ChildApplications.Add(new Application(cs));
- }
- }
- LogLevel.Information.Trace(Name, "Initialization : Completed.");
- }
-
- /// <summary>
- /// Determines whether an instance of the application exists as a dependant application.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="version">The version.</param>
- public Boolean IsChildApplication(String name, String version)
- {
- foreach ( var a in ChildApplications )
- if ( ( a.Name == name && a.Version == version ) || a.IsChildApplication(name, version) )
- return true;
- return false;
- }
-
- #endregion
- #region | EVENTS
-
- /// <summary>
- /// Performs initialization and configuration of resources for this and all dependant applications.
- /// </summary>
- public void OnStart()
- {
- //i| Process all dependant application's dependencies and configuration first.
- foreach ( Application application in ChildApplications )
- application.OnStart();
-
- //i| Process application dependencies.
- if ( Dependencies != null && Dependencies.HasElements )
- ProcessDependencies();
-
- //i| Process application configuration.
- if ( Config != null && Config.HasElements )
- ProcessConfiguration();
- }
-
- /// <summary>
- /// Manages the execution of process and dependant processes for the configured accelerator application instance.
- /// </summary>
- public void OnRun()
- {
- //i| Process dependant applications first.
- foreach (Application application in ChildApplications)
- application.OnRun();
-
- //i| Launch all defined processes for this application.
- foreach ( XElement e in XAppConfig.XPathSelectElements("runtime/onStart/process").ToList() )
- if ( e.IsEnabled() ) ProcessStartElement(e);
-
- //i| Launch hosted web servers.
- foreach ( XElement e in XAppConfig.XPathSelectElements("runtime/onStart/webServer").ToList() )
- if ( e.IsEnabled() ) ProcessWebServerElement(e);
-
- //i| Process blob to local storage synch
- foreach (XElement e in XAppConfig.XPathSelectElements("runtime/onStart/cloudSync").ToList())
- if (e.IsEnabled()) ProcessBlobSyncElement(e);
- }
-
- /// <summary>
- /// Manages the running components.
- /// </summary>
- public void OnRunning()
- {
- //i| Process dependant applications first.
- foreach (Application application in ChildApplications )
- application.OnRunning();
-
- //i| Process and runtime application timers.
- foreach ( XElement e in XAppConfig.XPathSelectElements("runtime/onRunning/timer").ToList() )
- if ( e.IsEnabled() ) ProcessTimerElement(e);
-
- //i| Process blob to local storage synch
- foreach (XElement e in XAppConfig.XPathSelectElements("runtime/onRunning/cloudSync").ToList())
- if (e.IsEnabled()) ProcessBlobSyncElement(e);
- }
-
- /// <summary>
- /// Performs the tear down of application environment and configuration files using Azure runtime service values.
- /// </summary>
- public void OnStop()
- {
- //i| Start all configuration 'graceful' tear-down processes.
- foreach ( XElement e in XAppConfig.XPathSelectElements("runtime/onStop/process").ToList() )
- if ( e.IsEnabled() ) ProcessStartElement(e);
-
- //i| Kill any processes we started that still remain.
- foreach ( var process in Processes.Values)
- if ( process != null && !process.HasExited )
- process.Protect(p => p.Kill());
- Processes.Clear();
-
- //i| Abort any blob synchronization timers.
- foreach ( var blobSync in BlobSyncInstances )
- blobSync.Stop();
- BlobSyncInstances.Clear();
-
- //i| Finally, do the same for each of our dependant applications.
- foreach ( Application application in ChildApplications )
- application.OnStop();
- }
-
- /// <summary>
- /// Performs the intialization of applications dependencies and resources.
- /// </summary>
- private void ProcessDependencies()
- {
- LogLevel.Information.Trace(Name, "Load Dependencies : Starting...");
-
- //i| Process local file storage and cache.
- foreach ( XElement e in Dependencies.Descendants("localStorage") )
- if (e.IsEnabled()) ProcessLocalStorageElement(e);
-
- //i| Process provisioned Endpoints
- foreach ( XElement e in Dependencies.Descendants("endPoint") )
- if (e.IsEnabled()) ProcessEndPointElement(e);
-
- //i| Process azure cloud drives.
- foreach ( XElement e in Dependencies.Descendants("cloudDrive") )
- if (e.IsEnabled()) ProcessCloudDriveElement(e);
-
- //i| Process blob to local storage synch
- foreach (XElement e in Dependencies.XPathSelectElements("cloudSync").ToList())
- if (e.IsEnabled()) ProcessBlobSyncElement(e);
-
- //i| Process local file copy elements (eg. read-only AppRole files to local storage)
- foreach ( XElement e in Dependencies.Descendants("localCache") )
- if (e.IsEnabled()) ProcessLocalCopyElement(e);
-
- //i| Perform file validation.
- foreach ( XElement e in Dependencies.Descendants("fileValidation") )
- if (e.IsEnabled()) ProcessFileValidationElement(e);
-
- LogLevel.Information.Trace(Name, "Load Dependencies : Completed.");
- }
-
- /// <summary>
- /// Performs the initialization of application environment and configuration files using Azure runtime service values.
- /// </summary>
- private void ProcessConfiguration()
- {
- LogLevel.Information.Trace(Name, "Configuration : Starting...");
-
- //i| Process environment variables.
- foreach ( XElement e in Config.Descendants("environmentVariable") )
- if (e.IsEnabled()) ProcessEnvironmentVariableElement(e);
-
- //i| Launch site web servers.
- foreach (XElement e in Config.XPathSelectElements("site").ToList())
- if (e.IsEnabled()) e.Protect(se => ProcessSiteElement(se));
-
- //i| Process configuration files.
- foreach (XElement e in Config.XPathSelectElements("files/file").ToList())
- if (e.IsEnabled()) ProcessFileConfigurationElement(e);
-
- LogLevel.Information.Trace(Name, "Configuration : Completed.");
- }
-
- #endregion
- #region | CONFIGURATION
-
- /// <summary>
- /// Processes the variable element.
- /// </summary>
- /// <param name="element">The element.</param>
- /// <param name="isParam">if set to <c>true</c> is param.</param>
- private static void ProcessVariableElement(XElement element, Boolean isParam)
- {
- String key = element.GetAttribute("key");
- String value = element.GetAttribute("value");
-
- //i| Parameters which are already set are not evaluated. This allows parent applications
- //i| the ability to override this class of variable in constituent dependant apps.
- if ( String.IsNullOrEmpty(value) || ( ServiceManager.Variables.ContainsKey(key) && isParam ) )
- return;
- ServiceManager.Variables[key] = value;
- }
-
- /// <summary>
- /// Processes the timer element for OnRunning interval processes (such as custom log collection to upload folder).
- /// </summary>
- /// <param name="element">The timer configuration element.</param>
- private void ProcessTimerElement(XElement element)
- {
- var config = new
- {
- TimerName = (String) new Variable(element.GetAttribute("name")),
- IntervalSeconds = (Int32) new Variable(element.GetAttribute("intervalInSeconds")),
- Processes = element.Descendants("process").OnValid(e => e.Where(p => p.IsEnabled()).ToList())
- };
- if ( config.Processes == null )
- return;
- LogLevel.Information.Trace(Name, "Running() : Creating interval processes execution timer : {{{{ Name: '{0}' }}, {{ IntervalSeconds: '{1}' }}}}.", config.TimerName, config.IntervalSeconds);
- new Timer(timerObject =>
- {
- LogLevel.Information.Trace(Name, "Running() : Interval process triggered : {{ '{0}' }}.", config.TimerName);
- foreach (XElement process in config.Processes)
- if (process.IsEnabled()) ProcessStartElement(process);
- },
- config,
- config.IntervalSeconds * 1000,
- config.IntervalSeconds * 1000);
- }
-
- /// <summary>
- /// Starts a process based on a process element configuration.
- /// </summary>
- /// <param name="element">The process configuration element.</param>
- private void ProcessStartElement(XElement element)
- {
- String processKey = new Variable(element.GetAttribute("processKey"));
- String command = Environment.ExpandEnvironmentVariables(new Variable(element.GetAttribute("command")));
- String args = Environment.ExpandEnvironmentVariables(new Variable(element.GetAttribute("args")));
- String workingDir = Environment.ExpandEnvironmentVariables(new Variable(element.GetAttribute("workingDir")));
- Boolean waitOnExit = new Variable(element.GetAttribute("waitOnExit") ?? "false");
- Int32 waitTimeoutInSeconds = new Variable(element.GetAttribute("waitTimeoutInSeconds") ?? "0");
- Int32 delayContinueInSeconds = element.AttributeAsVariable("delayContinueInSeconds", "0");
-
- LogLevel.Information.Trace(
- Name,
- "Process : Creating Process : {{{{ {0} }}, {{ '{1} {2}' }}, {{ WorkingDirectory: '{3}' }}, {{ DelaySeconds: '{4}' }}}}.",
- processKey,
- command,
- args,
- workingDir,
- delayContinueInSeconds
- );
- Processes[processKey] = RunProcess(processKey, command, workingDir, args, waitOnExit, waitTimeoutInSeconds);
- LogLevel.Information.Trace(Name, "Process : {0} : Waiting for '{1}' seconds before continue...", processKey, delayContinueInSeconds);
- Thread.Sleep(delayContinueInSeconds * 1000);
- }
-
- /// <summary>
- /// Processes the IIS site element (full IIS support).
- /// </summary>
- /// <param name="element">The configuration element.</param>
- private void ProcessSiteElement(XElement element)
- {
- String siteNameKey = new Variable(element.GetAttribute("siteNameKey", "SiteName"));
- String virtualPathKey = new Variable(element.GetAttribute("virtualPath", "VirtualPath"));
- String physicalPathKey = new Variable(element.GetAttribute("physicalPath", "PhysicalPath"));
- String physicalFolderNameKey = new Variable(element.GetAttribute("physicalFolderNameKey", "PhysicalFolderName"));
- String appPoolKey = new Variable(element.GetAttribute("appPoolKey", "AppPoolName"));
- String siteFolderKey = new Variable(element.GetAttribute("siteFolderKey", "SiteFolderName"));
- RuntimeVersion runtimeVersion = element.GetAttribute("runtime").ToEnum<RuntimeVersion>() ?? RuntimeVersion.v2;
- Boolean enableClassicPipelineMode = new Variable(element.GetAttribute("enableClassicPipelineMode") ?? "false");
- ServiceManager.Variables["replacementSitePath"] = new Variable(element.GetAttribute("replacementSitePath", "$(IisDrive)\\$(SiteName)$(VirtualPath)"));
- String replacePathChar = new Variable(element.GetAttribute("replacePathChar") ?? ".");
-
- //i|
- using (ServerManager sm = new ServerManager())
- {
- foreach(Site s in sm.Sites.Where(site => site.Name.StartsWith(RoleEnvironment.CurrentRoleInstance.Id)))
- {
- //i|
- //i| Populate variables which will be used by file verification and custom processes.
- //i|
- ServiceManager.Variables[siteNameKey] = s.Name.Replace(RoleEnvironment.CurrentRoleInstance.Id + "_", "");
-
- //i|
- //i| Update host bindings
- //i|
- foreach (Binding b in s.Bindings)
- {
- if (b.IsIPPortHostBinding)
- {
- ServiceManager.HostBindings[b.Host] = b.EndPoint.Address;
- }
- }
-
- //i|
- //i| Perform file validation.
- //i|
- Boolean valid = true;
- element.Descendants("fileValidation").Where(fv => fv.IsEnabled()).ForEach(fv => valid = valid && ProcessFileValidationElement(fv));
- if (!valid)
- {
- LogLevel.Warning.Trace(Name, "Site : Cannot find site file match; aborting site config.");
- }
- else
- {
- LogLevel.Information.Trace(Name, "Configuring site [{0}].", ServiceManager.Variables[siteNameKey]);
-
- //i|
- //i| Traverse site application and physical directories.
- //i|
- foreach (var a in s.Applications)
- {
- ServiceManager.Variables[appPoolKey] = a.ApplicationPoolName; //i| Set the variable for each application.
- ServiceManager.Variables[virtualPathKey] = a.Path;
- LogLevel.Information.Trace(Name, "Configuring application pool [{0}].", ServiceManager.Variables[appPoolKey]);
-
- ApplicationPool ap = sm.ApplicationPools[a.ApplicationPoolName];
-
- //i|
- //i| Update ASP.NET version: only if 4.0 (otherwise its simply the absence thereof)
- //i|
- LogLevel.Information.Trace(Name, "Managed runtime version is [{0}].", ap.ManagedRuntimeVersion);
- if (runtimeVersion.ToAppHostVersionString() != ap.ManagedRuntimeVersion)
- {
- LogLevel.Information.Trace(Name, "Setting runtime version [{0}] -> [{1}].", ap.ManagedRuntimeVersion, runtimeVersion.ToAppHostVersionString() );
- ap.ManagedRuntimeVersion = runtimeVersion.ToAppHostVersionString();
- }
-
- //i|
- //i| Update classic pipeline mode.
- //i|
- LogLevel.Information.Trace(Name, "Managed pipeline mode is [{0}].", ap.ManagedPipelineMode.ToString());
- if (enableClassicPipelineMode && (ap.ManagedPipelineMode != ManagedPipelineMode.Classic))
- {
- LogLevel.Information.Trace(Name, "Setting pipeline mode [{0}] -> [{1}]", ManagedPipelineMode.Classic.ToString());
- ap.ManagedPipelineMode = ManagedPipelineMode.Classic;
- }
-
- //i|
- //i| Move physical directories.
- //i|
- String parentVirtualPath = ServiceManager.Variables[virtualPathKey];
- foreach (var v in a.VirtualDirectories)
- {
- String physicalPath = v.PhysicalPath;
- try
- {
- String relativeVirtualPath = Path.Combine(a.Path, v.Path);
- String relativePhysicalPath = relativeVirtualPath.Replace("//", "~").Replace("/", ".");
- ServiceManager.Variables[virtualPathKey] = relativePhysicalPath; // (parentVirtualPath.EnsureEndsWith("/") + v.Path).Replace(@"//", @"~").Replace("/", replacePathChar).EnsureStartsWith("~");
- ServiceManager.Variables[physicalPathKey] = physicalPath;
- ServiceManager.Variables[siteFolderKey] = Directory.GetParent(v.PhysicalPath).Name;
- Path replacement = ServiceManager.Variables["replacementSitePath"].ToString().SetPathChar('\\');
- LogLevel.Information.Trace(Name, @"Redirecting Virtual Directory [{0}] from [{1}] -> [{2}]", ServiceManager.Variables[virtualPathKey], ServiceManager.Variables[physicalPathKey], replacement);
-
- if (!Directory.Exists(replacement))
- {
- LogLevel.Warning.Trace(Name, @"Target directory does not exist [{0}].", replacement);
- LogLevel.Information.Trace(Name, @"Creating target directory and migrating solution files.");
- Path commandPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Scripts\copySite.bat");
- String args = string.Format(@"""{0}"" ""{1}"" {2} /onlyOnEmpty", physicalPath, replacement, ServiceManager.Variables[appPoolKey]);
- ConsoleProcess.Start(commandPath, ".", true, args);
- //ConsoleProcess.CopyFolder(physicalPath, replacement);
- LogLevel.Information.Trace(Name, @"Web site migration complete.");
- }
- LogLevel.Information.Trace(Name, @"Redirecting [{0}] -> [{1}]", ServiceManager.Variables[physicalPathKey], replacement);
- v.PhysicalPath = v.PhysicalPath.Replace(physicalPath, replacement);
- }
- catch (Exception ex)
- {
- LogLevel.Error.TraceException(Name, ex, "An exception occured moving the vitual directory: Site will be run from existing solution folder.");
- v.PhysicalPath = physicalPath;
- }
- try
- {
- //i| Process site configuration files.
- foreach (XElement e in element.XPathSelectElements("files/file").ToList())
- if (e.IsEnabled()) ProcessFileConfigurationElement(e);
-
- //i|
- //i| Run customized site processes.
- //i|
- foreach (XElement process in element.Descendants("process").Where(e => e.IsEnabled()))
- {
-
- //x| RunProcess("copySite", commandPath, ".", args, true, 0);
- LogLevel.Information.Trace(Name, "Starting application specific configuration process.");
- ProcessStartElement(process);
- }
- }
- catch (Exception ex)
- {
- LogLevel.Error.TraceException(Name, ex, "An exception occured when running the configuration process.");
- }
- }
- }
-
- }
- }
- LogLevel.Information.Trace(Name, "Commiting changes to IIS.");
- sm.CommitChanges();
- ServiceManager.Variables["HostBindings"] = String.Concat(ServiceManager.HostBindings.Select(b => String.Format("\n{0} {1}", b.Value, b.Key)).ToArray());
- }
- }
-
- /// <summary>
- /// Processes the web server element (hosted web core). The hosted web core is started by the
- /// Service Manager after all defined applications have processed their individual OnStart
- /// definitions.
- /// </summary>
- /// <param name="element">The configuration element.</param>
- private static void ProcessWebServerElement(XElement element)
- {
- if ( new Variable(element.GetAttribute("enablePhp") ?? "false") )
- {
- //b| NOTE:
- //b|
- //b| This comment belongs elsewhere, but I am writing it now and don't want to burn
- //b| bandwidth to figure out where it belongs. So, please understand that the comment
- //b| below is much larger in breadth and scope than the place at which I am typing
- //b| it would reasonably indicate. (i|rdm)
- //b|
- //i| If true, the PHPRC environment variable must be set to the PHP home directory
- //i| prior to runtime start of the hosted web core. The easiest way to do this
- //i| is to make the PHP application definition a child dependency of the hosted web
- //i| core application.
- //i|
- //i| This is done is serveral sample apps such as Wordpress (via IIS); where the
- //i| the definition includes IIS and PHP dependencies; and then sets 'enablePhp'
- //i| to true in its own web core definition. (Its web core definitions extends the
- //i| base definitions provided by including the aforementioned child IIS dependency.)
- //i|
- //i| Of course, the Wordpress (via IIS) application section could declare all of the
- //i| IIS (webcore) and PHP related resources in its own definition; and forego any
- //i| child dependecies, but whats the fun in that? (Or the reusability?)
- //i|
- //i| Breaking out the definitions for applications such as IIS and PHP enable them
- //i| to be used in multiple instances (such as hosting dozens of ASP.NET apps) in a
- //i| clean and modular fashion. With definitions building upon themselves and playing
- //i| well together. (Or at least as well as I can in 5 weeks worth of work.)
- //i|
- //i| Wordpress (via Apache) would declare Apache and PHP dependencies; and would start
- //i| the Apache httpd process (and not declare a web core element at all).
- ServiceManager.WebServer.IsPhpEnabled = true;
- }
-
- //i| Once the value has been set to true (requiring a Classic Mode pipeline), it remains true.
- //i| This prevents other dependant, parent or separate applications from removing this resource
- //i| after initially requested.
- ServiceManager.WebServer.IsClassicModeEnabled = new Variable(element.GetAttribute("enableClassicPipelineMode") ?? "false");
-
- //i| Add all web applications (they will all share the same application pool.) There is only
- //i| one hosted web core per, but it may host an indefinite number of applications ( using
- //i| the same appplication pool).
- foreach (XElement e in element.Elements("application"))
- ServiceManager.WebServer.AddApplication(
- new Variable(e.GetAttribute("applicationPath")),
- new Variable(e.GetAttribute("physicalPath")),
- new Variable(e.GetAttribute("virtualDirectory") ?? "/")
- );
-
- //i| Add all unique bindings. ( Any duplicates bindings added by this or other active applications'
- //i| definitions will simply be ignored, )
- foreach ( XElement e in element.Elements("binding") )
- ServiceManager.WebServer.Bindings.Add(
- new WebServerBinding
- {
- Address = new Variable(e.GetAttribute("address")),
- Port = new Variable(e.GetAttribute("port")),
- HostHeader = new Variable(e.GetAttribute("host")),
- Protocol = new Variable(e.GetAttribute("protocol") ?? "Http").ToString().ToEnum<WebServerBinding.ProtocolType>()
- }
- );
- }
-
- /// <summary>
- /// Processes the local storage element.
- /// </summary>
- /// <param name="element">The element.</param>
- private void ProcessLocalStorageElement(XElement element)
- {
- String resourceName = new Variable(element.GetAttribute("configurationResourceName"));
- String pathKey = new Variable(element.GetAttribute("pathKey"));
- String maxSizeKey = new Variable(element.GetAttribute("maximumSizeKey"));
-
- LocalResource storage = RoleEnvironment.GetLocalResource(resourceName);
- LocalResources.Add(pathKey, storage);
- ServiceManager.Variables[pathKey] = storage.RootPath.TrimEnd('\\', '/', ' ');
- ServiceManager.Variables[maxSizeKey] = storage.MaximumSizeInMegabytes.ToString();
-
- LogLevel.Information.Trace(Name, "LocalStorage : Loaded : {{{{ Name: '{0}' }}, {{ Path, '{1}' }}, {{ Size: '{2}' }}}}.", resourceName, ServiceManager.Variables[pathKey], ServiceManager.Variables[maxSizeKey]);
- }
-
- /// <summary>
- /// Processes the end point element.
- /// </summary>
- /// <param name="element">The element.</param>
- private void ProcessEndPointElement(XElement element)
- {
- String endPointName = new Variable(element.GetAttribute("configurationEndPointName"));
- String portKey = new Variable(element.GetAttribute("portKey") ?? ( element.GetAttribute("configurationEndPointName") + "Port" ));
- String addressKey = new Variable(element.GetAttribute("addressKey") ?? ( element.GetAttribute("configurationEndPointName") + "Address" ));
-
- IPEndPoint localEndPoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints[endPointName].IPEndpoint;
- ServiceManager.Variables[portKey] = localEndPoint.Port.ToString();
- ServiceManager.Variables[addressKey] = localEndPoint.Address.ToString();
-
- LogLevel.Information.Trace(Name, "EndPoint : Loaded : {{{{ Name: '{0}' }}, {{ Address: '{1}' }}, {{ Port: '{2}' }}}}.", endPointName, ServiceManager.Variables[addressKey], ServiceManager.Variables[portKey]);
- }
-
- /// <summary>
- /// Processes the cloud drive element.
- /// </summary>
- /// <param name="element">The element.</param>
- private void ProcessCloudDriveElement(XElement element)
- {
- String pathKey = new Variable(element.GetAttribute("pathKey"));
- String connectionString = new Variable(element.GetAttribute("connectionString", "$(AcceleratorConnectionString)"));
- String pageBlobUri = new Variable(element.GetAttribute("pageBlobUri", "$(AcceleratorDrivePageBlobUri)"));
- Boolean createIfNotExists = new Variable(element.GetAttribute("createIfNotExist", "true"));
- Int32 createSizeInMB = new Variable(element.GetAttribute("createSizeInMB", "2048"));
- Int32 cacheSizeInMB = new Variable(element.GetAttribute("cacheSizeInMB", "1024"));
- Boolean readOnly = new Variable(element.GetAttribute("readOnly", "false"));
- Boolean copyBlob = new Variable(element.GetAttribute("copyBlob", "true"));
- String drivePath = null;
-
- LogLevel.Information.Trace(Name, "CloudDrive:\n\t{0:30}: {1}\n\t{2:30}: {3}\n\t{4:30}: {5}\n\t{6:30}: {7}\n", "PathKey", pathKey, "CloudDriveUri", pageBlobUri, "CreateIfNotExists", createIfNotExists, "ConnectionString", connectionString);
-
- ServiceManager.InitializeCloudDriveCache(); //i| Initialize Cache (once for all drives)
- try
- {
- //i|
- //i| Get cloud storage account.
- //i|
- CloudStorageAccount account;
- if ( !ServiceManager.IsRunningInCloud )
- {
- LogLevel.Information.Trace(Name, "CloudDrive running in Development Fabric (using local storage connection for clouddrive).");
- account = CloudStorageAccount.DevelopmentStorageAccount;
-
- //i| The first time running in the local dev fabric; create an empty drive. The new drive will show up as a drive letter,
- //i| manually copy the application files to the new drive. In all subsequent tests using the engine you're able to then
- //i| simulate the cloud based startup and teardown locally.
- createIfNotExists = true;
- }
- else
- {
- String cs = ( connectionString.Contains("DefaultEndpointsProtocol=") ) //i| A variable could be a connection string or a setting.
- ? connectionString
- : RoleEnvironment.GetConfigurationSettingValue(connectionString);
- cs = cs.Replace("https", "http"); //i| Cloud drives cannot use https; change if neccessary.
- account = CloudStorageAccount.Parse(cs);
- }
- //LogLevel.Information.TraceContent(Name, account.ToTraceString() ?? String.Empty, "CloudDrive : Account : ");
- //if (!account.CreateCloudBlobClient().GetBlobReference(pageBlobUri).Exists() && pageBlobUri.EndsWith("DrivePageBlobUri"))
- // pageBlobUri = DefaultSettings.CloudDriveUri;
-
- //i|
- //i| Check if already mounted.
- //i|
- CloudBlobClient client = account.CreateCloudBlobClient();
- CloudBlob blob = client.GetBlobReference(pageBlobUri);
- Boolean blobExists = blob.Exists();
- LogLevel.Information.Trace(Name, "CloudDrive configuration:\n\t{0:30}: {1}\n\t{2:30}: {3}\n\t{4:30}: {5}\n\t{6:30}: {7}\n", "Uri", blob.Uri.ToString(), "ContainerName", blob.Container.Name, "ContainerExists", blob.Container.Exists(), "BlobExists", blobExists);
- if (!blobExists)
- {
- LogLevel.Warning.Trace(Name, "CloudDrive pageblob not found.\n");
- if (createIfNotExists)
- {
- blob.Container.CreateIfNotExist();
- }
- else if (!pageBlobUri.EndsWith(".vhd"))
- {
- blob = client.GetBlobReference(DefaultSettings.CloudDriveUri);
- blobExists = blob.Exists();
- if (blobExists)
- {
- LogLevel.Information.Trace(Name, "CloudDrive using accelerator default location: '{0}'", DefaultSettings.CloudDriveUri);
- }
- else
- {
- throw new Exception("CloudDrive blob not found! Verify configuration settings.");
- }
- }
- }
-
- String driveKey = blob.Uri.ToString();
- if (ServiceManager.DrivePaths.ContainsKey(driveKey))
- {
- LogLevel.Information.Trace(Name, "CloudDrive already mounted and available at '{0}'.", ServiceManager.CloudDrives[driveKey].LocalPath);
- }
- else
- {
- //i|
- //i| New drive to mount.
- //i|
- CloudDrive drive = account.CreateCloudDrive(pageBlobUri);
-
- //i|
- //i| If specified, create a new empty cloud drive.
- //i|
- if (createIfNotExists)
- {
- blob.Container.CreateIfNotExist();
- drive.Protect(d => d.Create(cacheSizeInMB)); //i| Exceptions may be throw on success or pre-existing. Always attempt to mount anyway, so swallow this.
- }
-
- //i|
- //i| Mount cloud drive.
- //i|
- const Int32 totalAttempts = 3;
- for (Int32 attempt = 1; attempt <= totalAttempts; attempt++) //i| Per known azure issue multiple retries may be required to force a drive to mount.
- {
- try
- { drive.Mount(cacheSizeInMB, DriveMountOptions.None); //ServiceManager.IsRunningInCloud ? DriveMountOptions.Force : DriveMountOptions.None);
- }
- catch (CloudDriveException ex)
- {
- if (attempt == totalAttempts)
- {
- drive = account.GetCloneDrive(drive, cacheSizeInMB);
-
- //if (copyBlob)
- //{
- // try
- // {
- // LogLevel.Error.TraceException(Name, ex, "CloudDrive failed to mount.\n{0}\n", drive.ToTraceString());
- // LogLevel.Information.Trace(Name, "Creating clouddrive snapshot.");
- // Uri snapshot = drive.Snapshot();
- // Uri instanceUri = new Uri(pageBlobUri + RoleEnvironment.CurrentRoleInstance.Id);
- // LogLevel.Information.Trace(Name, "Creating read-only drive from snapshot: '{0}'; Instance: '{1}'", snapshot.AbsoluteUri, instanceUri.AbsoluteUri);
- // CloudDrive snapshotDrive = account.CreateCloudDrive(snapshot.AbsoluteUri);
- // LogLevel.Information.Trace(Name, "Coping snapshot drive to instance blob. Snapshot: '{0}'; Instance: '{1}'", snapshot.AbsoluteUri, instanceUri.AbsoluteUri);
- // snapshotDrive.CopyTo(new Uri(pageBlobUri + RoleEnvironment.CurrentRoleInstance.Id));
- // snapshotDrive.Unmount();
- // drive = account.CreateCloudDrive(instanceUri.ToString());
- // drive.Mount(cacheSize, DriveMountOptions.None);
- // drive.Protect(d => d.Create(cacheSize)); //i| Exceptions may be throw on success or pre-existing. Always attempt to mount anyway, so swallow this.
- // break;
- // }
- // catch (CloudDriveException cdex)
- // {
- // LogLevel.Error.TraceException(Name, ex, "Unable to mount snapshot drive.");
- // break;
- // }
- //}
- //LogLevel.Error.TraceException(Name, ex, "Unable to mount drive {0} of {1} attempts.", attempt, totalAttempts);
- //break;
- }
- else
- {
- LogLevel.Error.TraceException(Name, ex, "CloudDrive failed to mount.");
- }
- }
- if (!String.IsNullOrEmpty(drive.LocalPath)) //i| Check drive path to determine success. (Possible to throw an exception; yet still have a valid mounted drive.)
- break;
- }
-
- LogLevel.Information.Trace(Name, "CloudDrive mounted and available at '{0}'.", drive);
-
- //i|
- //i| Add to global collection of mounted drives; keying by the full absolute blob URI.
- //i|)
- if (!String.IsNullOrEmpty(drive.LocalPath))
- {
- drivePath = drive.LocalPath.TrimEnd('\\');
- ServiceManager.CloudDrives.Add(driveKey, drive);
- LogLevel.Information.Trace(Name, "CloudDrive mounted to drive: [{0}]", drivePath);
- }
- else
- {
- drivePath = ServiceManager.LocalStoragePath;
- LogLevel.Warning.Trace(Name, "Unable to mount drive; using local storage: [{0}]", drivePath);
- }
- ServiceManager.DrivePaths.Add(driveKey, drivePath);
- }
-
- //i|
- //i| Add path variable. (Get drive from global collection since drive may be newly mounted or previously existing.)
- //i|
- ServiceManager.Variables[pathKey] = ServiceManager.DrivePaths[driveKey];
- LogLevel.Information.Trace(Name, "CloudDrive available at: [{0}]", ServiceManager.Variables[pathKey]);
- }
- catch (Exception ex)
- {
- ServiceManager.ServiceState = ServiceState.MissingDependency;
- LogLevel.Error.TraceException(Name, ex, "An unknown failure occured mounting drive; see the exception details for additional information.");
- }
- }
-
- /// <summary>
- /// Processes the blob storage sync element.
- /// </summary>
- /// <param name="element">The element.</param>
- private void ProcessBlobSyncElement(XElement element)
- {
- Boolean ignoreAdditionalFiles = new Variable(element.GetAttribute("ignoreAdditionalFiles") ?? "true");
- SyncProviderType providerType = ((String)new Variable(element.GetAttribute("providerType"))).ToEnum<SyncProviderType>() ?? SyncProviderType.CloudSync;
- SyncManager syncManager = new SyncManager(providerType)
- {
- ContainerName = (String) new Variable(element.GetAttribute("blobDirectoryUri")),
- LocalPath = (String) new Variable(element.GetAttribute("localDirectoryPath")),
- Interval = TimeSpan.FromSeconds(new Variable(element.GetAttribute("intervalInSeconds", "0"))),
- SyncDirection = ((String)new Variable(element.GetAttribute("direction"))).ToEnum<SyncDirectionOrder>() ?? SyncDirectionOrder.Download,
- Account = CloudStorageAccount.Parse(new Variable(element.GetAttribute("connectionString")))
- };
-
- if ( syncManager.Interval != TimeSpan.Zero ) //i| Spawns a thread to sync on timed interval.
- {
- syncManager.Start();
- BlobSyncInstances.Add(syncManager);
- }
- else //i| Sync once then continue.
- {
- syncManager.Sync();
- }
- }
-
- /// <summary>
- /// Processes a file copy between local locations. (eg. From approot read-only upload location to local drive storage.)
- /// </summary>
- /// <param name="element">The element.</param>
- private void ProcessLocalCopyElement(XElement element)
- {
- String source = new Variable(element.GetAttribute("source"));
- String destination = new Variable(element.GetAttribute("destination"));
-
- LogLevel.Information.Trace(Name, "FileCopy : Starting : {{{{ Source: '{0}' }}, {{ Destination, '{1}' }}}}.", source, destination);
- Int32 filesCopied = CopyDirectory(source, destination, 0);
- LogLevel.Information.Trace(Name, "FileCopy : Finished : '{0}' files copied.", filesCopied);
- }
-
- /// <summary>
- /// Processes the file validation element.
- /// </summary>
- /// <param name="element">The element.</param>
- private Boolean ProcessFileValidationElement(XElement element)
- {
- String path = new Variable(element.GetAttribute("path"));
- Boolean checkAccess = new Variable(element.GetAttribute("checkAccess") ?? "false");
- Boolean required = new Variable(element.GetAttribute("required") ?? "false");
-
- if ( File.Exists(path) )
- {
- if ( !checkAccess )
- {
- LogLevel.Information.Trace(Name, "FileCheck : Verified : {{ '{0}' }}.", path);
- }
- else
- {
- var ac = File.GetAccessControl(path);
- var at = File.GetAttributes(path);
- LogLevel.Information.TraceContent(Name,
- String.Format(
- "\r\n\t[(\"ACCESS CONTROL\" )]\r\n{0}\r\n\t[(\"ATTRIBUTES\" )]\r\n{1}",
- ac.ToString("\t[ {0} : {1} ]\r\n", true, true),
- at.ToString("\t[ {0} : {1} ]\r\n", true)
- ), String.Format("FileCheck : Validated : {{ '{0}' }}.", path));
- }
- return true;
- }
- else if (required)
- {
- ServiceManager.ServiceState = ServiceState.MissingDependency;
- LogLevel.Error.Trace(Name, "FileCheck : File Not Found : {{ '{0}' }}.", path);
- }
- else
- …
Large files files are truncated, but you can click here to view the full file