Skip to content

Commit

Permalink
Added flag to support install extension to wwwroot.
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaomin Wu committed Mar 10, 2015
1 parent 34b5fe5 commit b0355d8
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 55 deletions.
8 changes: 6 additions & 2 deletions Kudu.Client/SiteExtensions/RemoteSiteExtensionManager.cs
Expand Up @@ -5,6 +5,7 @@
using System.Threading.Tasks;
using System.Web;
using Kudu.Client.Infrastructure;
using Kudu.Contracts.SiteExtensions;
using Kudu.Services.Arm;
using Newtonsoft.Json.Linq;

Expand Down Expand Up @@ -114,11 +115,15 @@ public async Task<HttpResponseMessage> GetLocalExtension(string id, bool checkLa
return (await Client.GetAsync(url.ToString())).EnsureSuccessful();
}

public async Task<HttpResponseMessage> InstallExtension(string id, string version = null, string feedUrl = null)
public async Task<HttpResponseMessage> InstallExtension(string id, string version = null, string feedUrl = null, SiteExtensionInfo.SiteExtensionType? type = null)
{
var json = new JObject();
json["version"] = version;
json["feed_url"] = feedUrl;
if (type.HasValue)
{
json["type"] = Enum.GetName(typeof(SiteExtensionInfo.SiteExtensionType), type.Value);
}

return (await Client.PutAsJsonAsync("siteextensions/" + id, json)).EnsureSuccessful();
}
Expand All @@ -128,7 +133,6 @@ public async Task<HttpResponseMessage> UninstallExtension(string id)
var url = new StringBuilder(ServiceUrl);
url.Append("siteextensions/");
url.Append(id);

return (await Client.DeleteAsync(url.ToString())).EnsureSuccessful();
}
}
Expand Down
2 changes: 1 addition & 1 deletion Kudu.Contracts/SiteExtensions/ISiteExtensionManager.cs
Expand Up @@ -17,7 +17,7 @@ public interface ISiteExtensionManager
/// <summary>
/// Install or update a site extension
/// </summary>
Task<SiteExtensionInfo> InstallExtension(string id, string version, string feedUrl, ITracer tracer);
Task<SiteExtensionInfo> InstallExtension(string id, string version, string feedUrl, SiteExtensionInfo.SiteExtensionType type, ITracer tracer);

Task<bool> UninstallExtension(string id);
}
Expand Down
8 changes: 5 additions & 3 deletions Kudu.Contracts/SiteExtensions/SiteExtensionInfo.cs
@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Net;
using Kudu.Contracts.Infrastructure;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using NuGet.Client.VisualStudio;

namespace Kudu.Contracts.SiteExtensions
Expand All @@ -14,7 +14,8 @@ public enum SiteExtensionType
{
Gallery,
PreInstalledMonaco,
PreInstalledEnabled
PreInstalledEnabled,
WebRoot
}

public SiteExtensionInfo()
Expand Down Expand Up @@ -73,7 +74,8 @@ public string Title
set;
}

[JsonIgnore]
[JsonProperty(PropertyName = "type")]
[JsonConverter(typeof(StringEnumConverter))]
public SiteExtensionType Type
{
get;
Expand Down
141 changes: 96 additions & 45 deletions Kudu.Core/SiteExtensions/SiteExtensionManager.cs
Expand Up @@ -41,6 +41,7 @@ public class SiteExtensionManager : ISiteExtensionManager
private const string _settingsFileName = "SiteExtensionSettings.json";
private const string _feedUrlSetting = "feed_url";
private const string _installUtcTimestampSetting = "install_timestamp_utc";
private const string _packageType = "package_type";
private const string _versionSetting = "version";

private readonly string _rootPath;
Expand Down Expand Up @@ -278,7 +279,7 @@ private SiteExtensionInfo GetPreInstalledExtension(string id)
}

// <inheritdoc />
public async Task<SiteExtensionInfo> InstallExtension(string id, string version, string feedUrl, ITracer tracer)
public async Task<SiteExtensionInfo> InstallExtension(string id, string version, string feedUrl, SiteExtensionInfo.SiteExtensionType type, ITracer tracer)
{
var installationLock = SiteExtensionInstallationLock.CreateLock(_environment.SiteExtensionSettingsPath, id);

Expand All @@ -287,7 +288,7 @@ public async Task<SiteExtensionInfo> InstallExtension(string id, string version,
// hold on to lock till action complete (success or fail)
return await installationLock.LockOperationAsync<SiteExtensionInfo>(async () =>
{
return await TryInstallExtension(id, version, feedUrl, tracer);
return await TryInstallExtension(id, version, feedUrl, type, tracer);
}, TimeSpan.Zero);
}
catch (Exception ex)
Expand All @@ -308,7 +309,7 @@ public async Task<SiteExtensionInfo> InstallExtension(string id, string version,
}
}

private async Task<SiteExtensionInfo> TryInstallExtension(string id, string version, string feedUrl, ITracer tracer)
private async Task<SiteExtensionInfo> TryInstallExtension(string id, string version, string feedUrl, SiteExtensionInfo.SiteExtensionType type, ITracer tracer)
{
SiteExtensionInfo info = null;
HttpStatusCode status = HttpStatusCode.OK; // final status when success
Expand All @@ -328,7 +329,7 @@ private async Task<SiteExtensionInfo> TryInstallExtension(string id, string vers
if (await this.IsSiteExtensionInstalled(id, version, feedUrl))
{
// package already installed, return package from local repo.
tracer.Trace("Site extension {0} with version {1} from {2} already installed.", id, version, feedUrl);
tracer.Trace("Package {0} with version {1} from {2} already installed.", id, version, feedUrl);
info = await GetLocalExtension(id);
alreadyInstalled = true;
}
Expand Down Expand Up @@ -360,11 +361,12 @@ private async Task<SiteExtensionInfo> TryInstallExtension(string id, string vers
using (tracer.Step("Install package: {0}.", id))
{
string installationDirectory = GetInstallationDirectory(id);
localPackage = await InstallExtension(repoPackage, installationDirectory, feedUrl, tracer);
localPackage = await InstallExtension(repoPackage, installationDirectory, feedUrl, type, tracer);
siteExtensionSettings.SetValues(new KeyValuePair<string, JToken>[] {
new KeyValuePair<string, JToken>(_versionSetting, localPackage.Identity.Version.ToNormalizedString()),
new KeyValuePair<string, JToken>(_feedUrlSetting, feedUrl),
new KeyValuePair<string, JToken>(_installUtcTimestampSetting, DateTime.UtcNow.ToString("u"))
new KeyValuePair<string, JToken>(_installUtcTimestampSetting, DateTime.UtcNow.ToString("u")),
new KeyValuePair<string, JToken>(_packageType, Enum.GetName(typeof(SiteExtensionInfo.SiteExtensionType), type))
});
}
}
Expand Down Expand Up @@ -431,7 +433,7 @@ private async Task<SiteExtensionInfo> TryInstallExtension(string id, string vers
/// <para>3. Deploy site extension job</para>
/// <para>4. Execute install.cmd if exist</para>
/// </summary>
private async Task<UIPackageMetadata> InstallExtension(UIPackageMetadata package, string installationDirectory, string feedUrl, ITracer tracer)
private async Task<UIPackageMetadata> InstallExtension(UIPackageMetadata package, string installationDirectory, string feedUrl, SiteExtensionInfo.SiteExtensionType type, ITracer tracer)
{
try
{
Expand All @@ -449,33 +451,47 @@ private async Task<UIPackageMetadata> InstallExtension(UIPackageMetadata package
string packageLocalFilePath = GetNuGetPackageFile(package.Identity.Id, package.Identity.Version.ToString());
using (tracer.Step("Download site extension: {0}", package.Identity))
{
await remoteRepo.DownloadPackageToFolder(package.Identity, installationDirectory, pathToLocalCopyOfNudpk: packageLocalFilePath);
}
string extractPath = installationDirectory;
if (SiteExtensionInfo.SiteExtensionType.WebRoot == type)
{
extractPath = _environment.WebRootPath;
FileSystemHelpers.EnsureDirectory(extractPath);

// If there is no xdt file, generate default.
using (tracer.Step("Check if applicationhost.xdt file existed."))
{
GenerateApplicationHostXdt(installationDirectory, '/' + package.Identity.Id, isPreInstalled: false, tracer: tracer);
}
tracer.Trace("Generate xdt file to map application to run from wwwroot when request for /{0}", package.Identity.Id);
GenerateRemapToWebRootApplicationHostXdt(installationDirectory, string.Format(CultureInfo.InvariantCulture, "/{0}", package.Identity.Id));
}

using (tracer.Step("Trigger site extension job"))
{
OperationManager.Attempt(() => DeploySiteExtensionJobs(package.Identity.Id));
await remoteRepo.DownloadPackageToFolder(package.Identity, extractPath, pathToLocalCopyOfNudpk: packageLocalFilePath);
}

var externalCommandFactory = new ExternalCommandFactory(_environment, _settings, installationDirectory);
string installScript = Path.Combine(installationDirectory, _installScriptName);
if (FileSystemHelpers.FileExists(installScript))
// ignore below action if we install packge to wwwroot
if (SiteExtensionInfo.SiteExtensionType.WebRoot != type)
{
using (tracer.Step("Execute install.cmd"))
// If there is no xdt file, generate default.
using (tracer.Step("Check if applicationhost.xdt file existed."))
{
OperationManager.Attempt(() =>
GenerateApplicationHostXdt(installationDirectory, '/' + package.Identity.Id, isPreInstalled: false, tracer: tracer);
}

using (tracer.Step("Trigger site extension job"))
{
OperationManager.Attempt(() => DeploySiteExtensionJobs(package.Identity.Id));
}

var externalCommandFactory = new ExternalCommandFactory(_environment, _settings, installationDirectory);
string installScript = Path.Combine(installationDirectory, _installScriptName);
if (FileSystemHelpers.FileExists(installScript))
{
using (tracer.Step("Execute install.cmd"))
{
Executable exe = externalCommandFactory.BuildCommandExecutable(installScript,
installationDirectory,
_settings.GetCommandIdleTimeout(), NullLogger.Instance);
exe.ExecuteWithProgressWriter(NullLogger.Instance, _traceFactory.GetTracer(), String.Empty);
});
OperationManager.Attempt(() =>
{
Executable exe = externalCommandFactory.BuildCommandExecutable(installScript,
installationDirectory,
_settings.GetCommandIdleTimeout(), NullLogger.Instance);
exe.ExecuteWithProgressWriter(NullLogger.Instance, _traceFactory.GetTracer(), String.Empty);
});
}
}
}
}
Expand Down Expand Up @@ -578,11 +594,20 @@ private static void GenerateApplicationHostXdt(string installationDirectory, str
tracer.Trace("Missing xdt file, creating one.");
}

string xdtContent = CreateDefaultXdtFile(relativeUrl, isPreInstalled);
string physicalPath = isPreInstalled ? "%XDT_LATEST_EXTENSIONPATH%" : "%XDT_EXTENSIONPATH%";
string xdtContent = CreateDefaultXdtFile(relativeUrl, physicalPath);
OperationManager.Attempt(() => FileSystemHelpers.WriteAllText(xdtPath, xdtContent));
}
}

private static void GenerateRemapToWebRootApplicationHostXdt(string extensionDirectory, string relativeUrl)
{
FileSystemHelpers.EnsureDirectory(extensionDirectory);
string xdtPath = Path.Combine(extensionDirectory, _applicationHostFile);
string xdtContent = CreateDefaultXdtFile(relativeUrl, @"%home%\site\wwwroot");
OperationManager.Attempt(() => FileSystemHelpers.WriteAllText(xdtPath, xdtContent));
}

public async Task<bool> UninstallExtension(string id)
{
ITracer tracer = _traceFactory.GetTracer();
Expand All @@ -597,27 +622,39 @@ public async Task<bool> UninstallExtension(string id)
throw new DirectoryNotFoundException(installationDirectory);
}

var externalCommandFactory = new ExternalCommandFactory(_environment, _settings, installationDirectory);
if (IsInstalledToWebRoot(id))
{
// special handling for package that install in wwwroot instead of under site extension folder
tracer.Trace("Clear all content in wwwroot");
FileSystemHelpers.DeleteDirectoryContentsSafe(_environment.WebRootPath);
}
else
{
// default action for site extension uninstall

// only site extension has below infomation
var externalCommandFactory = new ExternalCommandFactory(_environment, _settings, installationDirectory);

string uninstallScript = Path.Combine(installationDirectory, _uninstallScriptName);
string uninstallScript = Path.Combine(installationDirectory, _uninstallScriptName);

if (FileSystemHelpers.FileExists(uninstallScript))
{
using (tracer.Step("Execute uninstall.cmd"))
if (FileSystemHelpers.FileExists(uninstallScript))
{
OperationManager.Attempt(() =>
using (tracer.Step("Execute uninstall.cmd"))
{
Executable exe = externalCommandFactory.BuildCommandExecutable(uninstallScript,
installationDirectory,
_settings.GetCommandIdleTimeout(), NullLogger.Instance);
exe.ExecuteWithProgressWriter(NullLogger.Instance, _traceFactory.GetTracer(), String.Empty);
});
OperationManager.Attempt(() =>
{
Executable exe = externalCommandFactory.BuildCommandExecutable(uninstallScript,
installationDirectory,
_settings.GetCommandIdleTimeout(), NullLogger.Instance);
exe.ExecuteWithProgressWriter(NullLogger.Instance, _traceFactory.GetTracer(), String.Empty);
});
}
}
}

using (tracer.Step("Remove site extension job"))
{
OperationManager.Attempt(() => CleanupSiteExtensionJobs(id));
using (tracer.Step("Remove site extension job"))
{
OperationManager.Attempt(() => CleanupSiteExtensionJobs(id));
}
}

using (tracer.Step("Delete site extension package, directory and arm settings"))
Expand Down Expand Up @@ -660,9 +697,8 @@ private static string GetPreInstalledDirectory(string id)
return Path.Combine(programFiles, "SiteExtensions", id);
}

private static string CreateDefaultXdtFile(string relativeUrl, bool isPreInstalled)
private static string CreateDefaultXdtFile(string relativeUrl, string physicalPath)
{
string physicalPath = isPreInstalled ? "%XDT_LATEST_EXTENSIONPATH%" : "%XDT_EXTENSIONPATH%";
string template = null;

Stream stream = typeof(SiteExtensionManager).Assembly.GetManifestResourceStream("Kudu.Core.SiteExtensions." + _applicationHostFile + ".xml");
Expand Down Expand Up @@ -760,6 +796,11 @@ private async Task<SiteExtensionInfo> ConvertLocalPackageToSiteExtensionInfo(UIP
}

var info = new SiteExtensionInfo(package);
if (IsInstalledToWebRoot(info.Id))
{
info.Type = SiteExtensionInfo.SiteExtensionType.WebRoot;
}

SetLocalInfo(info);
await TryCheckLocalPackageLatestVersionFromRemote(info, checkLatest);
return info;
Expand Down Expand Up @@ -849,6 +890,16 @@ private string GetFullUrl(string url)
return url == null ? null : new Uri(new Uri(_baseUrl), url).ToString().Trim('/') + "/";
}

private bool IsInstalledToWebRoot(string packageId)
{
JsonSettings siteExtensionSettings = GetSettingManager(packageId);
string packageTypeStr = siteExtensionSettings.GetValue(_packageType);
SiteExtensionInfo.SiteExtensionType packageType;

return (Enum.TryParse<SiteExtensionInfo.SiteExtensionType>(packageTypeStr, true, out packageType)
&& SiteExtensionInfo.SiteExtensionType.WebRoot == packageType);
}

/// <summary>
/// Create SourceRepository from given feed endpoint
/// </summary>
Expand Down

0 comments on commit b0355d8

Please sign in to comment.