diff --git a/src/SIM.Base/XmlDocumentEx.cs b/src/SIM.Base/XmlDocumentEx.cs index 6bbb3627..d66bbb65 100644 --- a/src/SIM.Base/XmlDocumentEx.cs +++ b/src/SIM.Base/XmlDocumentEx.cs @@ -212,6 +212,12 @@ public void SetElementValue(string xpath, string value) element.InnerText = value; } + public bool ElementAttributeContains(string xpath, string attributeName, string substring) + { + string value = this.GetElementAttributeValue(xpath, attributeName).ToLower(); + return !string.IsNullOrEmpty(value) && value.Contains(substring.ToLower()); + } + #endregion #region Private methods diff --git a/src/SIM.Instances/Instance.cs b/src/SIM.Instances/Instance.cs index 259e5c7f..82574d16 100644 --- a/src/SIM.Instances/Instance.cs +++ b/src/SIM.Instances/Instance.cs @@ -29,6 +29,8 @@ public class Instance : Website, IXmlSerializable private Product _Product; + private ICollection _instanceRoles; + #endregion #region Constructors @@ -167,7 +169,6 @@ public virtual bool IsSitecoreEnvironmentMember } } } - return false; } catch (Exception ex) @@ -494,6 +495,78 @@ public InstanceType Type } } + /// + /// A collection of server roles this instance is defined as + /// + public ICollection Roles + { + get + { + if (_instanceRoles != null) + { + return _instanceRoles; + } + + _instanceRoles = new List(); + + try + { + //Check if sitecore identity server + if (this.GetWebConfig().ElementAttributeContains("/configuration/system.webServer/aspNetCore", "arguments", + "sitecore.identityServer.host")) + { + _instanceRoles.Add(InstanceRole.IdentityServer); + return _instanceRoles; + } + + //Check if xconnect + string instanceName = this.Name.ToLower(); + if (instanceName.Contains(".xconnect")) + { + _instanceRoles.Add(InstanceRole.XConnect); + return _instanceRoles; + } + + //Check if sitecore publishing service + if (this.GetWebConfig().ElementAttributeContains("/configuration/system.webServer/aspNetCore", "processPath", + "sitecore.framework.publishing.host")) + { + _instanceRoles.Add(InstanceRole.PublishingService); + return _instanceRoles; + } + + //Find role by 'role:define' key in web.config + string roleDefine = this.GetWebConfig() + .GetElementAttributeValue("/configuration/appSettings/add[@key='role:define']", "value").ToLower(); + if (string.IsNullOrEmpty(roleDefine)) + { + _instanceRoles.Add((InstanceRole + .Unknown)); //If 'role:define' is not present, this is likely a pre-version 9 solution, and we don't know the role + return _instanceRoles; + } + + //Add any and all roles that are found in the role:define key + foreach (InstanceRole role in Enum.GetValues(typeof(InstanceRole)).Cast()) + { + if (roleDefine.Contains(role.ToString().ToLower())) + { + _instanceRoles.Add(role); + } + } + } + catch (Exception ex) + { + _instanceRoles.Add(InstanceRole.Unknown); + Log.Error(ex, "Instance role could not be resolved and was set to 'Unknown'"); + } + return _instanceRoles; + } + } + + public bool IsContentManagementInstance => this.Roles.Contains(InstanceRole.Standalone) || + this.Roles.Contains(InstanceRole.ContentManagement) || + this.Roles.Contains(InstanceRole.Unknown); + public enum InstanceType { Sitecore8AndEarlier, @@ -503,6 +576,21 @@ public enum InstanceType Unknown } + public enum InstanceRole + { + Standalone, + ContentManagement, + ContentDelivery, + Reporting, + Processing, + Indexing, + DedicatedDispatch, + XConnect, + IdentityServer, + PublishingService, + Unknown + } + #endregion #region Public methods diff --git a/src/SIM.Pipelines/Install/Containers/GenerateEnvironmentData.cs b/src/SIM.Pipelines/Install/Containers/GenerateEnvironmentData.cs index f836cd3b..34c4dbb2 100644 --- a/src/SIM.Pipelines/Install/Containers/GenerateEnvironmentData.cs +++ b/src/SIM.Pipelines/Install/Containers/GenerateEnvironmentData.cs @@ -59,7 +59,7 @@ protected override void Process([NotNull] ProcessorArgs arguments) string environmentName = args.EnvModel.ProjectName; Assert.ArgumentNotNullOrEmpty(environmentName, "projectName"); - this.AddEnvironment(this.CreateEnvironment(environmentName, destinationFolder)); + SitecoreEnvironmentHelper.AddSitecoreEnvironment(this.CreateEnvironment(environmentName, destinationFolder)); } private SitecoreEnvironment CreateEnvironment(string environmentName, string destinationFolder) @@ -110,19 +110,5 @@ private string GetMemberType(string serviceName) return null; } - - private void AddEnvironment(SitecoreEnvironment environment) - { - foreach (SitecoreEnvironment se in SitecoreEnvironmentHelper.SitecoreEnvironments) - { - if (se.Name == environment.Name) - { - return; - } - } - - SitecoreEnvironmentHelper.SitecoreEnvironments.Add(environment); - SitecoreEnvironmentHelper.SaveSitecoreEnvironmentData(SitecoreEnvironmentHelper.SitecoreEnvironments); - } } } diff --git a/src/SIM.Pipelines/Install/GenerateSitecoreEnvironmentData.cs b/src/SIM.Pipelines/Install/GenerateSitecoreEnvironmentData.cs index 79f5d149..58e7ccd6 100644 --- a/src/SIM.Pipelines/Install/GenerateSitecoreEnvironmentData.cs +++ b/src/SIM.Pipelines/Install/GenerateSitecoreEnvironmentData.cs @@ -27,7 +27,7 @@ protected override void Process([NotNull] ProcessorArgs args) InstallParam sqlDbPrefixParam = arguments.Tasker.GlobalParams.FirstOrDefault(p => p.Name == SqlDbPrefix); if (sqlDbPrefixParam != null && !string.IsNullOrEmpty(sqlDbPrefixParam.Value)) { - this.AddSitecoreEnvironment(this.CreateSitecoreEnvironment(arguments.Tasker, sqlDbPrefixParam.Value,arguments.UnInstallDataPath)); + SitecoreEnvironmentHelper.AddSitecoreEnvironment(this.CreateSitecoreEnvironment(arguments.Tasker, sqlDbPrefixParam.Value,arguments.UnInstallDataPath)); } } @@ -52,20 +52,5 @@ private SitecoreEnvironment CreateSitecoreEnvironment(Tasker tasker, string sqlD return sitecoreEnvironment; } - - private void AddSitecoreEnvironment(SitecoreEnvironment sitecoreEnvironment) - { - // Do not add new Sitecore environment if its name already exists in the Environments.json file - foreach (SitecoreEnvironment se in SitecoreEnvironmentHelper.SitecoreEnvironments) - { - if (se.Name == sitecoreEnvironment.Name) - { - return; - } - } - - SitecoreEnvironmentHelper.SitecoreEnvironments.Add(sitecoreEnvironment); - SitecoreEnvironmentHelper.SaveSitecoreEnvironmentData(SitecoreEnvironmentHelper.SitecoreEnvironments); - } } } \ No newline at end of file diff --git a/src/SIM.Pipelines/PipelinesConfig.cs b/src/SIM.Pipelines/PipelinesConfig.cs index 284066bd..b60f5fcb 100644 --- a/src/SIM.Pipelines/PipelinesConfig.cs +++ b/src/SIM.Pipelines/PipelinesConfig.cs @@ -266,6 +266,34 @@ public static class PipelinesConfig + + + + + + + + + + + + + + + + "; } -} +} \ No newline at end of file diff --git a/src/SIM.Pipelines/PublishingService/Commands.cs b/src/SIM.Pipelines/PublishingService/Commands.cs new file mode 100644 index 00000000..596f2491 --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Commands.cs @@ -0,0 +1,43 @@ +using System.Management.Automation; + +namespace SIM.Pipelines.PublishingService +{ + public static class Commands + { + private const string SET_CONNECTION_STRING = "configuration setconnectionstring {0} \"{1}\""; + private const string SCHEMA_UPGRADE = "schema upgrade --force"; + private const string SCHEMA_RESET = "schema reset --force"; + private const string IIS_INSTALL = "iis install --sitename \"{0}\" --apppool \"{1}\" --port \"{2}\" --hosts --force"; + + public static string CommandRoot { get; set; } + + public static void SetConnectionString(string connectionStringName, string connectionStringValue) + { + ExecuteCommand(SET_CONNECTION_STRING, connectionStringName, connectionStringValue); + } + + public static void SchemaUpgrade() + { + ExecuteCommand(SCHEMA_UPGRADE); + } + + public static void SchemaReset() + { + ExecuteCommand(SCHEMA_RESET); + } + + public static void IISInstall(string siteName, string appPoolName, int port) + { + ExecuteCommand(IIS_INSTALL, siteName, appPoolName, port); + } + + private static void ExecuteCommand(string commandArgs, params object[] args) + { + using (PowerShell PS = PowerShell.Create()) + { + PS.AddScript($"Set-Location {CommandRoot}").Invoke(); + PS.AddScript(string.Format(".\\Sitecore.Framework.Publishing.Host.exe " + commandArgs, args)).Invoke(); + } + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Install/AddToEnvironmentProcessor.cs b/src/SIM.Pipelines/PublishingService/Install/AddToEnvironmentProcessor.cs new file mode 100644 index 00000000..4a558b1e --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Install/AddToEnvironmentProcessor.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using SIM.SitecoreEnvironments; +using Sitecore.Diagnostics.Logging; + +namespace SIM.Pipelines.PublishingService.Install +{ + public class AddToEnvironmentProcessor : SPSProcessor + { + protected override void ProcessCore(InstallSPSProcessorArgs args) + { + try + { + SitecoreEnvironment se = SitecoreEnvironmentHelper.GetExistingOrNewSitecoreEnvironment(args.CMInstance.Name); + if (se.Members == null) + { + se.Members = new List(); + } + + se.Members.Add(new SitecoreEnvironmentMember(args.SPSSiteName, SitecoreEnvironmentMember.Types.Site.ToString())); + SitecoreEnvironmentHelper.AddOrUpdateSitecoreEnvironment(se); + } + catch (Exception ex) + { + Log.Warn(ex, $"Exception thrown while adding new SPS instance to CM environment:\n{ex}"); + } + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Install/CreateIISiteProcessor.cs b/src/SIM.Pipelines/PublishingService/Install/CreateIISiteProcessor.cs new file mode 100644 index 00000000..48a47cad --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Install/CreateIISiteProcessor.cs @@ -0,0 +1,10 @@ +namespace SIM.Pipelines.PublishingService.Install +{ + public class CreateIISiteProcessor : SPSProcessor + { + protected override void ProcessCore(InstallSPSProcessorArgs args) + { + Commands.IISInstall(args.SPSSiteName, args.SPSSiteName, args.SPSPort); + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Install/InstallSPSProcessorArgs.cs b/src/SIM.Pipelines/PublishingService/Install/InstallSPSProcessorArgs.cs new file mode 100644 index 00000000..030b0828 --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Install/InstallSPSProcessorArgs.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.Data.SqlClient; +using SIM.Instances; +using SIM.Pipelines.Processors; + +namespace SIM.Pipelines.PublishingService.Install +{ + public class InstallSPSProcessorArgs : ProcessorArgs + { + #region Properties + public Instance CMInstance { get; set; } + public string InstanceFolder { get; set; } + public string SPSInstanceFolder { get; set; } + public string SqlAdminUsername { get; set; } + public string SqlAdminPassword { get; set; } + public string SPSPackagePath { get; set; } + public string SPSSiteName { get; set; } + public string SPSAppPoolName { get; set; } + public string SPSWebroot { get; set; } + public int SPSPort { get; set; } + public Dictionary SPSConnectionStrings { get; set; } + + #endregion + } +} diff --git a/src/SIM.Pipelines/PublishingService/Install/PrepareInstallationProcessor.cs b/src/SIM.Pipelines/PublishingService/Install/PrepareInstallationProcessor.cs new file mode 100644 index 00000000..7cb60b07 --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Install/PrepareInstallationProcessor.cs @@ -0,0 +1,13 @@ +using System.IO.Compression; + +namespace SIM.Pipelines.PublishingService.Install +{ + public class PrepareInstallationProcessor : SPSProcessor + { + protected override void ProcessCore(InstallSPSProcessorArgs args) + { + ZipFile.ExtractToDirectory(args.SPSPackagePath, args.SPSWebroot); + Commands.CommandRoot = args.SPSWebroot; + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Install/ResetDatabaseSchemaProcessor.cs b/src/SIM.Pipelines/PublishingService/Install/ResetDatabaseSchemaProcessor.cs new file mode 100644 index 00000000..405f75ef --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Install/ResetDatabaseSchemaProcessor.cs @@ -0,0 +1,11 @@ +namespace SIM.Pipelines.PublishingService.Install +{ + public class ResetDatabaseSchemaProcessor : SPSProcessor + { + //Resetting the schema is done to clear any potentially existing Publishing tables + protected override void ProcessCore(InstallSPSProcessorArgs args) + { + Commands.SchemaReset(); + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Install/SetActualConnectionStringsProcessor.cs b/src/SIM.Pipelines/PublishingService/Install/SetActualConnectionStringsProcessor.cs new file mode 100644 index 00000000..8050bcf2 --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Install/SetActualConnectionStringsProcessor.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Data.SqlClient; + +namespace SIM.Pipelines.PublishingService.Install +{ + public class SetActualConnectionStringsProcessor : SPSProcessor + { + protected override void ProcessCore(InstallSPSProcessorArgs args) + { + foreach (KeyValuePair connString in args.SPSConnectionStrings) + { + Commands.SetConnectionString(connString.Key, connString.Value.ToString()); + } + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Install/SetAdminConnectionStringsProcessor.cs b/src/SIM.Pipelines/PublishingService/Install/SetAdminConnectionStringsProcessor.cs new file mode 100644 index 00000000..d076e1cf --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Install/SetAdminConnectionStringsProcessor.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Data.SqlClient; + +namespace SIM.Pipelines.PublishingService.Install +{ + public class SetAdminConnectionStringsProcessor : SPSProcessor + { + //Using the admin credentials for connection strings is necessary to upgrade and reset the database schema + protected override void ProcessCore(InstallSPSProcessorArgs args) + { + foreach (KeyValuePair connString in args.SPSConnectionStrings) + { + string AdminConnString = new SqlConnectionStringBuilder(connString.Value.ToString()) + { + UserID = args.SqlAdminUsername, + Password = args.SqlAdminPassword + }.ToString(); + + Commands.SetConnectionString(connString.Key, AdminConnString); + } + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Install/UpgradeDatabaseSchemaProcessor.cs b/src/SIM.Pipelines/PublishingService/Install/UpgradeDatabaseSchemaProcessor.cs new file mode 100644 index 00000000..913ce6ac --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Install/UpgradeDatabaseSchemaProcessor.cs @@ -0,0 +1,10 @@ +namespace SIM.Pipelines.PublishingService.Install +{ + public class UpgradeDatabaseSchemaProcessor : SPSProcessor + { + protected override void ProcessCore(InstallSPSProcessorArgs args) + { + Commands.SchemaUpgrade(); + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Install/VerifyInstallationProcessor.cs b/src/SIM.Pipelines/PublishingService/Install/VerifyInstallationProcessor.cs new file mode 100644 index 00000000..91b7e90f --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Install/VerifyInstallationProcessor.cs @@ -0,0 +1,37 @@ +using System.Diagnostics; +using System.Net; +using Newtonsoft.Json.Linq; +using Sitecore.Diagnostics.Logging; + +namespace SIM.Pipelines.PublishingService.Install +{ + public class VerifyInstallationProcessor : SPSProcessor + { + protected override void ProcessCore(InstallSPSProcessorArgs args) + { + string statusEndpoint = $"http://{args.SPSSiteName}:{args.SPSPort}/api/publishing/operations/status"; + if (!ValidateStatusEndpoint(statusEndpoint)) + { + Log.Error("Publishing Service returned errors"); + } + OpenStatusEndpoint(statusEndpoint); + } + + private bool ValidateStatusEndpoint(string statusEndpoint) + { + Log.Info($"HTTP GET {statusEndpoint}"); + JObject response; + using (WebClient wc = new WebClient()) + { + response = JObject.Parse(wc.DownloadString(statusEndpoint)); + } + + return response["status"].Value().Equals(0); + } + + private void OpenStatusEndpoint(string statusEndpoint) + { + System.Diagnostics.Process.Start(new ProcessStartInfo(statusEndpoint)); + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/SPSProcessor.cs b/src/SIM.Pipelines/PublishingService/SPSProcessor.cs new file mode 100644 index 00000000..4c2b8e17 --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/SPSProcessor.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using SIM.Pipelines.Processors; + +namespace SIM.Pipelines.PublishingService +{ + public abstract class SPSProcessor : Processor where T : ProcessorArgs + { + protected override void Process([NotNull] ProcessorArgs args) + { + this.ProcessCore((T)args); + } + + protected abstract void ProcessCore([NotNull] T args); + } +} diff --git a/src/SIM.Pipelines/PublishingService/Uninstall/RemoveAppPoolProcessor.cs b/src/SIM.Pipelines/PublishingService/Uninstall/RemoveAppPoolProcessor.cs new file mode 100644 index 00000000..61646a56 --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Uninstall/RemoveAppPoolProcessor.cs @@ -0,0 +1,56 @@ +using System; +using System.Linq; +using System.Threading; +using JetBrains.Annotations; +using Microsoft.Web.Administration; + +namespace SIM.Pipelines.PublishingService.Uninstall +{ + public class RemoveAppPoolProcessor : SPSProcessor + { + protected override void ProcessCore([NotNull] UninstallSPSProcessorArgs args) + { + if (args.SkipSPSAppPool) + { + return; + } + + using (ServerManager sm = new ServerManager()) + { + ApplicationPool spsAppPool = sm.ApplicationPools.FirstOrDefault(a => a.Name.Equals(args.SPSAppPoolName)); + + if (!DeleteAppPool(sm, spsAppPool, args.MaxRetries, args.RetryInterval)) + { + throw new Exception($"Could not stop application pool with the name {args.SPSAppPoolName}. Please remove it manually in IIS."); + } + + sm.CommitChanges(); + } + } + + private bool DeleteAppPool(ServerManager sm, [CanBeNull] ApplicationPool appPool, int maxRetries, int retryInterval) + { + if (appPool != null) + { + int retries = 0; + while (!appPool.State.Equals(ObjectState.Stopped) && retries < maxRetries) + { + //If the site is starting/stopping IIS won't accept commands and will throw an error, we'll check if its in a started state first + if (appPool.State.Equals(ObjectState.Started)) + { + appPool.Stop(); + } + retries++; + Thread.Sleep(retryInterval); + } + if (!appPool.State.Equals(ObjectState.Stopped)) + { + return false; + } + sm.ApplicationPools.Remove(appPool); + } + return true; + } + + } +} diff --git a/src/SIM.Pipelines/PublishingService/Uninstall/RemoveFromEnvironmentProcessor.cs b/src/SIM.Pipelines/PublishingService/Uninstall/RemoveFromEnvironmentProcessor.cs new file mode 100644 index 00000000..8c3ec1cf --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Uninstall/RemoveFromEnvironmentProcessor.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; +using SIM.SitecoreEnvironments; +using Sitecore.Diagnostics.Logging; + +namespace SIM.Pipelines.PublishingService.Uninstall +{ + public class RemoveFromEnvironmentProcessor : SPSProcessor + { + protected override void ProcessCore(UninstallSPSProcessorArgs args) + { + try + { + SitecoreEnvironmentMember SPSMember = args.Instance.SitecoreEnvironment.Members.FirstOrDefault(member => member.Name == args.SPSSiteName); + if (SPSMember != null) + { + args.Instance.SitecoreEnvironment.Members.Remove(SPSMember); + } + SitecoreEnvironmentHelper.SaveSitecoreEnvironmentData(SitecoreEnvironmentHelper.SitecoreEnvironments); + } + catch (Exception ex) + { + Log.Warn(ex, $"Exception thrown while removing SPS instance from CM environment:\n{ex}"); + } + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Uninstall/RemoveIISSiteProcessor.cs b/src/SIM.Pipelines/PublishingService/Uninstall/RemoveIISSiteProcessor.cs new file mode 100644 index 00000000..555d6c08 --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Uninstall/RemoveIISSiteProcessor.cs @@ -0,0 +1,56 @@ +using System; +using System.Linq; +using System.Threading; +using JetBrains.Annotations; +using Microsoft.Web.Administration; + +namespace SIM.Pipelines.PublishingService.Uninstall +{ + public class RemoveIISSiteProcessor : SPSProcessor + { + protected override void ProcessCore([NotNull] UninstallSPSProcessorArgs args) + { + if (args.SkipSPSSite) + { + return; + } + + using (ServerManager sm = new ServerManager()) + { + Site spsSite = sm.Sites.FirstOrDefault(s => s.Name.Equals(args.SPSSiteName)); + + if (!DeleteSite(sm, spsSite, args.MaxRetries, args.RetryInterval)) + { + throw new Exception($"Could not stop site with the name {args.SPSSiteName}. Please remove it manually in IIS."); + } + + sm.CommitChanges(); + } + } + + private bool DeleteSite(ServerManager sm, [CanBeNull] Site site, int maxRetries, int retryInterval) + { + if (site != null) + { + int retries = 0; + while (!site.State.Equals(ObjectState.Stopped) && retries < maxRetries) + { + //If the site is starting/stopping IIS won't accept commands and will throw an error, we'll check if its in a started state first + if (site.State.Equals(ObjectState.Started)) + { + site.Stop(); + } + retries++; + Thread.Sleep(retryInterval); + } + if (!site.State.Equals(ObjectState.Stopped)) + { + return false; + } + sm.Sites.Remove(site); + } + return true; + } + + } +} diff --git a/src/SIM.Pipelines/PublishingService/Uninstall/RemoveWebrootFolderProcessor.cs b/src/SIM.Pipelines/PublishingService/Uninstall/RemoveWebrootFolderProcessor.cs new file mode 100644 index 00000000..03d5743d --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Uninstall/RemoveWebrootFolderProcessor.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using JetBrains.Annotations; + +namespace SIM.Pipelines.PublishingService.Uninstall +{ + public class RemoveWebrootFolderProcessor : SPSProcessor + { + protected override void ProcessCore([NotNull] UninstallSPSProcessorArgs args) + { + if (args.SkipSPSWebroot) + { + return; + } + + if (Directory.Exists(args.SPSWebroot)) + { + try + { + Directory.Delete(args.SPSWebroot, true); + } + catch (IOException ex) + { + //Unlikely user scenario, but it can occur when you create an instance, then immediately try to overwrite it without restarting SIM + throw new Exception($"SIM may be locking the {args.SPSWebroot} folder if it was just created. Try restarting SIM and running the operation again", ex); + } + } + } + } +} diff --git a/src/SIM.Pipelines/PublishingService/Uninstall/UninstallSPSProcessorArgs.cs b/src/SIM.Pipelines/PublishingService/Uninstall/UninstallSPSProcessorArgs.cs new file mode 100644 index 00000000..9cc21b90 --- /dev/null +++ b/src/SIM.Pipelines/PublishingService/Uninstall/UninstallSPSProcessorArgs.cs @@ -0,0 +1,21 @@ +using SIM.Instances; +using SIM.Pipelines.Processors; + +namespace SIM.Pipelines.PublishingService.Uninstall +{ + public class UninstallSPSProcessorArgs : ProcessorArgs + { + #region Properties + public Instance Instance { get; set; } + public string SPSSiteName { get; set; } + public string SPSAppPoolName { get; set; } + public string SPSWebroot { get; set; } + public bool SkipSPSSite { get; set; } + public bool SkipSPSAppPool { get; set; } + public bool SkipSPSWebroot { get; set; } + public int MaxRetries { get; set; } = 6; + public int RetryInterval { get; set; } = 5000; + + #endregion + } +} diff --git a/src/SIM.Pipelines/SIM.Pipelines.csproj b/src/SIM.Pipelines/SIM.Pipelines.csproj index 638aa939..0d2f71a7 100644 --- a/src/SIM.Pipelines/SIM.Pipelines.csproj +++ b/src/SIM.Pipelines/SIM.Pipelines.csproj @@ -60,6 +60,9 @@ ..\packages\mongocsharpdriver.1.10.1\lib\net35\MongoDB.Driver.dll True + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + ..\packages\Sitecore.Diagnostics.Base.2.0.1.171\lib\Sitecore.Diagnostics.Base.dll @@ -105,6 +108,17 @@ + + + + + + + + + + + @@ -215,6 +229,11 @@ + + + + + diff --git a/src/SIM.Pipelines/packages.config b/src/SIM.Pipelines/packages.config index 80369967..e313ec4f 100644 --- a/src/SIM.Pipelines/packages.config +++ b/src/SIM.Pipelines/packages.config @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/src/SIM.Products/ProductHelper.cs b/src/SIM.Products/ProductHelper.cs index 1b4dccb7..ad20032c 100644 --- a/src/SIM.Products/ProductHelper.cs +++ b/src/SIM.Products/ProductHelper.cs @@ -97,6 +97,13 @@ public static string GetKernelPath([NotNull] string webRootPath) return Path.Combine(webRootPath, "bin\\Sitecore.Kernel.dll"); } + [NotNull] + public static string GetPublishingHostPath([NotNull] string webRootPath) + { + Assert.ArgumentNotNullOrEmpty(webRootPath, nameof(webRootPath)); + + return Path.Combine(webRootPath, "Sitecore.Framework.Publishing.Host.exe"); + } #endregion #region Methods diff --git a/src/SIM.SitecoreEnvironments/SitecoreEnvironmentHelper.cs b/src/SIM.SitecoreEnvironments/SitecoreEnvironmentHelper.cs index b391f305..1ca8e564 100644 --- a/src/SIM.SitecoreEnvironments/SitecoreEnvironmentHelper.cs +++ b/src/SIM.SitecoreEnvironments/SitecoreEnvironmentHelper.cs @@ -113,5 +113,35 @@ public static bool TryGetEnvironmentById(Guid environmentId, [CanBeNull]out Site return environment != null; } + + public static void AddSitecoreEnvironment(SitecoreEnvironment sitecoreEnvironment) + { + // Do not add new Sitecore environment if its name already exists in the Environments.json file + foreach (SitecoreEnvironment se in SitecoreEnvironments) + { + if (se.Name == sitecoreEnvironment.Name) + { + return; + } + } + + SitecoreEnvironments.Add(sitecoreEnvironment); + SaveSitecoreEnvironmentData(SitecoreEnvironments); + } + + public static void AddOrUpdateSitecoreEnvironment(SitecoreEnvironment sitecoreEnvironment) + { + SitecoreEnvironment Environment = SitecoreEnvironments.FirstOrDefault(se => se.Name == sitecoreEnvironment.Name); + if (Environment == null) + { + AddSitecoreEnvironment(sitecoreEnvironment); + } + else + { + int envIndex = SitecoreEnvironments.IndexOf(Environment); + SitecoreEnvironments[envIndex] = sitecoreEnvironment; + SaveSitecoreEnvironmentData(SitecoreEnvironments); + } + } } } \ No newline at end of file diff --git a/src/SIM.Tool.Base/Configuration/Configuration.cs b/src/SIM.Tool.Base/Configuration/Configuration.cs new file mode 100644 index 00000000..d4a11b9e --- /dev/null +++ b/src/SIM.Tool.Base/Configuration/Configuration.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; +using System.IO; +using Newtonsoft.Json.Linq; + +namespace SIM.Tool.Base.Configuration +{ + public class Configuration + { + private JObject settingsDoc; + #region Singleton + private static Configuration instance; + private Configuration() + { + string globalSettings = string.Empty; + using (StreamReader reader = + new StreamReader(Path.Combine(Directory.GetCurrentDirectory(), "GlobalParamsConfig/GlobalSettings.json"))) + { + globalSettings = reader.ReadToEnd(); + } + + this.settingsDoc = JObject.Parse(globalSettings); + + } + public static Configuration Instance + { + get + { + if (instance == null) + { + instance = new Configuration(); + } + + return instance; + } + } + #endregion + + public Dictionary SolrMap + { + get + { + return this.settingsDoc["SolrVersionsMap"].ToObject>(); + } + } + + public Dictionary GlobalFilesMap + { + get + { + return this.settingsDoc["GlobalFilesMap"].ToObject>(); + } + } + + public IEnumerable ExcludedUninstallParams + { + get + { + return settingsDoc["ExcludeUninstallParams"].Values(); + } + } + + public Dictionary SitecorePublishingServiceCompatibility => this.settingsDoc["SitecorePublishingServiceCompatibility"].ToObject>(); + } +} diff --git a/src/SIM.Tool.Base/Pipelines/InstallSPSWizardArgs.cs b/src/SIM.Tool.Base/Pipelines/InstallSPSWizardArgs.cs new file mode 100644 index 00000000..891c8e13 --- /dev/null +++ b/src/SIM.Tool.Base/Pipelines/InstallSPSWizardArgs.cs @@ -0,0 +1,84 @@ +using SIM.Pipelines.PublishingService.Install; + +namespace SIM.Tool.Base.Pipelines +{ + using System.Collections.Generic; + using System.Data.SqlClient; + using System.IO; + using SIM.Adapters.WebServer; + using SIM.Instances; + using SIM.Pipelines.Processors; + using SIM.Tool.Base.Profiles; + using SIM.Tool.Base.Wizards; + public class InstallSPSWizardArgs : WizardArgs + { + private string _spsPackagePath; + public InstallSPSWizardArgs(Instance cmInstance) + { + Initialize(cmInstance); + } + + #region Properties + + //From Instance + public Instance CMInstance { get; private set; } + public string InstanceName { get; private set; } + public ConnectionStringCollection InstanceConnectionStrings { get; private set; } + + //From Profile + public string InstanceFolder { get; set; } + public string SPSInstanceFolder { get; set; } + + public string SqlAdminUsername { get; private set; } + public string SqlAdminPassword { get; private set; } + + + //From User Input + public string SPSPackage + { + get { return Path.GetFileNameWithoutExtension(_spsPackagePath); } + set { _spsPackagePath = value; } + } + + public int SPSVersionInt { get; set; } + public string SPSName { get; set; } + public int SPSPort { get; set; } + public Dictionary SPSConnectionStrings { get; set; } = new Dictionary(); + + #endregion + + #region Methods + private void Initialize(Instance instance) + { + this.CMInstance = instance; + this.InstanceName = instance.Name; + this.InstanceFolder = ProfileManager.Profile.InstancesFolder; + this.InstanceConnectionStrings = instance.Configuration.ConnectionStrings; + this.SPSInstanceFolder = ProfileManager.Profile.InstancesFolder; + + SqlConnectionStringBuilder SqlServerConnectionString = ProfileManager.GetConnectionString(); + this.SqlAdminUsername = SqlServerConnectionString.UserID; + this.SqlAdminPassword = SqlServerConnectionString.Password; + } + + public override ProcessorArgs ToProcessorArgs() + { + return new InstallSPSProcessorArgs() + { + CMInstance = this.CMInstance, + SPSSiteName = this.SPSName, + SPSAppPoolName = this.SPSName, + SPSWebroot = Path.Combine(this.SPSInstanceFolder, this.SPSName), + SPSPort = this.SPSPort, + InstanceFolder = this.InstanceFolder, + SPSInstanceFolder = this.SPSInstanceFolder, + SPSPackagePath = this._spsPackagePath, + SqlAdminUsername = this.SqlAdminUsername, + SqlAdminPassword = this.SqlAdminPassword, + SPSConnectionStrings = this.SPSConnectionStrings + }; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/SIM.Tool.Base/Pipelines/UninstallSPSWizardArgs.cs b/src/SIM.Tool.Base/Pipelines/UninstallSPSWizardArgs.cs new file mode 100644 index 00000000..bba27bb0 --- /dev/null +++ b/src/SIM.Tool.Base/Pipelines/UninstallSPSWizardArgs.cs @@ -0,0 +1,54 @@ +using SIM.Pipelines.PublishingService.Uninstall; + +namespace SIM.Tool.Base.Pipelines +{ + using System; + using System.Collections.Generic; + using System.Data.SqlClient; + using System.IO; + using SIM.Adapters.WebServer; + using SIM.Instances; + using SIM.Pipelines.Install; + using SIM.Pipelines.Processors; + using SIM.Services; + using SIM.Tool.Base.Converters; + using SIM.Tool.Base.Profiles; + using SIM.Tool.Base.Wizards; + public class UninstallSPSWizardArgs : WizardArgs + { + public UninstallSPSWizardArgs(Instance instance) + { + this.Instance = instance; + this.InstanceName = instance.Name; + } + + #region Properties + + public Instance Instance { get; private set; } + public string InstanceName { get; private set; } + public string SPSInstanceFolder { get; } = ProfileManager.Profile.InstancesFolder; + + public string SPSSiteName { get; set; } + public string SPSAppPoolName { get; set; } + public string SPSWebroot { get; set; } + public bool SkipSPSSite { get; set; } + public bool SkipSPSAppPool { get; set; } + public bool SkipSPSWebroot { get; set; } + + public override ProcessorArgs ToProcessorArgs() + { + return new UninstallSPSProcessorArgs() + { + Instance = this.Instance, + SPSSiteName = this.SPSSiteName, + SPSAppPoolName = this.SPSAppPoolName, + SPSWebroot = this.SPSWebroot, + SkipSPSSite = this.SkipSPSSite, + SkipSPSAppPool = this.SkipSPSAppPool, + SkipSPSWebroot = this.SkipSPSWebroot + }; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/SIM.Tool.Base/SIM.Tool.Base.csproj b/src/SIM.Tool.Base/SIM.Tool.Base.csproj index 5da84edc..e3fbd03a 100644 --- a/src/SIM.Tool.Base/SIM.Tool.Base.csproj +++ b/src/SIM.Tool.Base/SIM.Tool.Base.csproj @@ -47,6 +47,9 @@ False True + + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll + ..\packages\Sitecore.Diagnostics.Base.2.0.1.171\lib\Sitecore.Diagnostics.Base.dll @@ -75,11 +78,14 @@ + + + @@ -201,6 +207,7 @@ +