Skip to content

Commit

Permalink
Using SignalR 2.0 #875, and adding PowerShell #877
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmelsayed committed Feb 10, 2014
1 parent e0d74e5 commit 870a005
Show file tree
Hide file tree
Showing 17 changed files with 129 additions and 76 deletions.
15 changes: 15 additions & 0 deletions Kudu.Core.Test/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.2.0" newVersion="2.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.2.0" newVersion="2.0.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
2 changes: 1 addition & 1 deletion Kudu.Core.Test/DebugConsoleFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public void Dispose()
_processes.Clear();
}

protected override IProcess CreateProcess(string connectionId)
protected override IProcess CreateProcess(string connectionId, string shell)
{
return _process;
}
Expand Down
3 changes: 2 additions & 1 deletion Kudu.Core.Test/Kudu.Core.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<HintPath>..\packages\Mercurial.Net.1.1.1.607\lib\net35-Client\Mercurial.Net.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.SignalR.Core">
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.1.1.3\lib\net40\Microsoft.AspNet.SignalR.Core.dll</HintPath>
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.2.0.1\lib\net45\Microsoft.AspNet.SignalR.Core.dll</HintPath>
</Reference>
<Reference Include="Moq, Version=4.2.1312.1622, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
Expand Down Expand Up @@ -131,6 +131,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Infrastructure\EnvironmentFacts.cs" />
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
Expand Down
2 changes: 1 addition & 1 deletion Kudu.Core.Test/packages.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Mercurial.Net" version="1.1.1.607" targetFramework="net40" />
<package id="Microsoft.AspNet.SignalR.Core" version="1.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.Core" version="2.0.1" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.1.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.1.0" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.6" targetFramework="net45" />
Expand Down
12 changes: 11 additions & 1 deletion Kudu.Services.Web/App_Start/NinjectServices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,16 @@
using Kudu.Services.Web.Services;
using Kudu.Services.Web.Tracing;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Ninject;
using Ninject.Activation;
using Ninject.Web.Common;
using Owin;
using XmlSettings;

[assembly: WebActivator.PreApplicationStartMethod(typeof(Kudu.Services.Web.App_Start.NinjectServices), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(Kudu.Services.Web.App_Start.NinjectServices), "Stop")]
[assembly: OwinStartup(typeof(Kudu.Services.Web.App_Start.NinjectServices.SignalRStartup))]

namespace Kudu.Services.Web.App_Start
{
Expand Down Expand Up @@ -284,7 +287,14 @@ private static void RegisterServices(IKernel kernel)

// Register the default hubs route: ~/signalr
GlobalHost.DependencyResolver = new SignalRNinjectDependencyResolver(kernel);
RouteTable.Routes.MapConnection<PersistentCommandController>("commandstream", "/commandstream");
}

public static class SignalRStartup
{
public static void Configuration(IAppBuilder app)
{
app.MapSignalR<PersistentCommandController>("/commandstream");
}
}

public class SignalRNinjectDependencyResolver : DefaultDependencyResolver
Expand Down
51 changes: 37 additions & 14 deletions Kudu.Services.Web/Content/Scripts/KuduExecV2.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

function SwitchConsole() {
var id = window.$KuduExecConsole.attr("id");
if (id === "KuduExecConsoleV2") {
Expand All @@ -19,10 +18,20 @@ window.KuduExec = { workingDir: curWorkingDir };
function LoadConsoleV2() {

var fileExplorerChanged = false;
//diretory change callback from FileBrowser.js
function _changeDir(value) {
//for the very first time, value is empty but we know that the file explorer root is appRoot
value = value || window.KuduExec.appRoot;
curWorkingDir(value);
_sendCommand("cd /d \"" + value + "\"");
if (getShell().toUpperCase() === "POWERSHELL") {
//PowerShell doesn't return a new line after CD, so let's add a new line in the UI
DisplayAndUpdate({ Error: "", Output: "\n" });
_sendCommand("cd \"" + value + "\"");
} else {
//CMD can't CD into different drives without /d and it's harmless for normal directories
_sendCommand("cd /d \"" + value + "\"");
}
//the change notification goes both ways (console <--> file explorer)
//the console uses this flag to break the loop
fileExplorerChanged = true;
};

Expand Down Expand Up @@ -63,6 +72,7 @@ function LoadConsoleV2() {
controller.resetHistory();
DisplayAndUpdate(lastLine);
lastLine.Output = "";
lastLine.Error = "";
DisplayAndUpdate(lastLine);
fileExplorerChanged = false;
if (line.trim().toUpperCase() == "EXIT") {
Expand Down Expand Up @@ -93,40 +103,50 @@ function LoadConsoleV2() {
autofocus: true,
animateScroll: true,
promptHistory: true,
welcomeMessage: "Kudu Remote Execution Console\r\nType 'exit' then hit 'enter' to get a new cmd.exe process.\r\nType 'cls' to clear the console\r\n\r\n"
welcomeMessage: "Kudu Remote Execution Console\r\nType 'exit' then hit 'enter' to get a new " + getShell() + " process.\r\nType 'cls' to clear the console\r\n\r\n"
});
window.$KuduExecConsole = $('#KuduExecConsoleV2');
window.$KuduExecConsole.append(kuduExecConsole);
if (getShell().toUpperCase() === "POWERSHELL") {
$("div.jquery-console-inner").css("background-color", "#012456");
}

var connection = $.connection('/commandstream');
var connection = $.connection('/commandstream', "shell=" + getShell(), true);
window.$KuduExecConsole.data('connection', connection);

connection.start({
waitForPageLoad: true,
transport: "auto"
});


connection.received(function (data) {
DisplayAndUpdate(data);
controller.enableInput();
});

function _sendCommand(input) {
_sendMessage(input);
}

function _sendMessage(input) {
function _sendCommand(input) {
connection.send(input);
}

function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}


function startsWith(str, prefix) {
return str.indexOf(prefix) == 0;
}

function getJSONValue(input) {
return input? (input.Output || input.Error || "").toString() : "";
}

function getShell() {
var regex = new RegExp("[\\?&]shell=([^&#]*)"),
results = regex.exec(location.search);
return results == null ? "CMD" : decodeURIComponent(results[1].replace(/\+/g, " "));
}

function DisplayAndUpdate(data) {
var prompt = getJSONValue(data);
var lastLinestr = getJSONValue(lastLine);
Expand Down Expand Up @@ -178,9 +198,12 @@ function LoadConsoleV2() {

//save last line for next time.
lastLine = data;

prompt = prompt.trim();
if (!endsWith(prompt, "\n") && endsWith(prompt, ">") && !fileExplorerChanged) {
var windowsPath = prompt.replace("\n", "").replace(">", "");
if (startsWith(windowsPath, "PS ")) {
windowsPath = windowsPath.substr(3);
}
if (windowsPath.match(/^[a-zA-Z]:(\\\w+)*(.*)$/)) {
if (!window.KuduExec.appRoot) {
window.KuduExec.appRoot = windowsPath;
Expand All @@ -197,4 +220,4 @@ function LoadConsoleV2() {

$(function () {
LoadConsoleV2();
})
})
10 changes: 0 additions & 10 deletions Kudu.Services.Web/Content/Scripts/jquery.signalR-1.1.3.min.js

This file was deleted.

8 changes: 8 additions & 0 deletions Kudu.Services.Web/Content/Scripts/jquery.signalR-2.0.1.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Kudu.Services.Web/Content/Styles/FileBrowser.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ div.console {
}

div.console div.jquery-console-inner {
background-color: #012456;
background-color: black;
color: white;
height: 100%;
margin-left: auto;
Expand Down
2 changes: 1 addition & 1 deletion Kudu.Services.Web/DebugConsole/Default.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
<script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.1.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
<script src="/content/scripts/jquery.signalr-1.1.3.min.js"></script>
<script src="/content/scripts/jquery.signalr-2.0.1.min.js"></script>
<script src="/content/scripts/kuduexec.js"></script>
<script src="/content/scripts/kuduexecV2.js"></script>
<script src="/content/scripts/filebrowser.js"></script>
Expand Down
17 changes: 10 additions & 7 deletions Kudu.Services.Web/Kudu.Services.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,20 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNet.SignalR.Core">
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.1.1.3\lib\net40\Microsoft.AspNet.SignalR.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.SignalR.Owin">
<HintPath>..\packages\Microsoft.AspNet.SignalR.Owin.1.1.3\lib\net45\Microsoft.AspNet.SignalR.Owin.dll</HintPath>
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.2.0.1\lib\net45\Microsoft.AspNet.SignalR.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.SignalR.SystemWeb">
<HintPath>..\packages\Microsoft.AspNet.SignalR.SystemWeb.1.1.3\lib\net45\Microsoft.AspNet.SignalR.SystemWeb.dll</HintPath>
<HintPath>..\packages\Microsoft.AspNet.SignalR.SystemWeb.2.0.1\lib\net45\Microsoft.AspNet.SignalR.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Owin">
<HintPath>..\packages\Microsoft.Owin.2.0.2\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb">
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.1.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.2.0.2\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security">
<HintPath>..\packages\Microsoft.Owin.Security.2.0.2\lib\net45\Microsoft.Owin.Security.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
Expand Down Expand Up @@ -113,12 +116,12 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Content Include="Content\Scripts\jquery.signalR-2.0.1.min.js" />
<Content Include="Content\Scripts\KuduExec.js" />
<Content Include="Content\Styles\FileBrowser.css" />
<Content Include="Content\Scripts\jquery-console\jquery.console.js" />
<Content Include="Content\Scripts\KuduExecV2.js" />
<Content Include="Content\Scripts\FileBrowser.js" />
<Content Include="Content\Scripts\jquery.signalR-1.1.3.min.js" />
<Content Include="JobRuns\history.html" />
<Content Include="Web.config">
<SubType>Designer</SubType>
Expand Down
8 changes: 8 additions & 0 deletions Kudu.Services.Web/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.2.0" newVersion="2.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.2.0" newVersion="2.0.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
8 changes: 7 additions & 1 deletion Kudu.Services.Web/_Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,13 @@
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="~/Env">Environment</a></li>
<li><a href="~/DebugConsole">Debug console</a></li>
<li class="dropdown">
<a href="#" data-toggle="dropdown" class="dropdown-toggle">Debug console <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="/DebugConsole">CMD</a></li>
<li><a href="/DebugConsole/?shell=powershell">PowerShell</a></li>
</ul>
</li>
<li><a href="~/dump">Diagnostic dump</a></li>
<li><a href="~/logstream" title="If no log events are being generated the page may not load.">Log stream</a></li>
<li><a href="~/WebHooks">Web hooks</a></li>
Expand Down
13 changes: 7 additions & 6 deletions Kudu.Services.Web/packages.config
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="jQuery" version="1.6.4" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="3.1.0" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR" version="1.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.Core" version="1.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.JS" version="1.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.Owin" version="1.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.SystemWeb" version="1.1.3" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR" version="2.0.1" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.Core" version="2.0.1" targetFramework="net45" />
<package id="Microsoft.AspNet.SignalR.SystemWeb" version="2.0.1" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.1.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.1.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.1.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="3.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="1.0.1" targetFramework="net45" />
<package id="Microsoft.Owin" version="2.0.2" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="2.0.2" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="2.0.2" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="5.0.8" targetFramework="net45" />
<package id="Ninject" version="3.0.1.10" targetFramework="net45" />
Expand Down
25 changes: 18 additions & 7 deletions Kudu.Services/Commands/PersistentCommandController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ public PersistentCommandController(IEnvironment environment, IDeploymentSettings

protected override Task OnConnected(IRequest request, string connectionId)
{
var shell = request.QueryString != null ? request.QueryString["shell"] : null;
using (_tracer.Step("Client connected with connectionId = " + connectionId))
{
_processes.GetOrAdd(connectionId, StartProcess);
_processes.GetOrAdd(connectionId, cId => StartProcess(cId, shell));

return base.OnConnected(request, connectionId);
}
Expand All @@ -60,9 +61,10 @@ protected override Task OnDisconnected(IRequest request, string connectionId)
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
ProcessInfo process;
var shell = request.QueryString != null ? request.QueryString["shell"] : null;
if (!_processes.TryGetValue(connectionId, out process) || process.Process.HasExited)
{
process = _processes.AddOrUpdate(connectionId, StartProcess, (s, p) => StartProcess(s));
process = _processes.AddOrUpdate(connectionId, cId => StartProcess(cId, shell), (s, p) => StartProcess(s, shell));
}
else
{
Expand All @@ -88,22 +90,31 @@ protected override Task OnReceived(IRequest request, string connectionId, string
return base.OnReceived(request, connectionId, data);
}

protected virtual IProcess CreateProcess(string connectionId)
protected virtual IProcess CreateProcess(string connectionId, string shell)
{
var externalCommandFactory = new ExternalCommandFactory(_environment, _settings, _environment.RootPath);
var exe = externalCommandFactory.BuildExternalCommandExecutable(_environment.RootPath, _environment.WebRootPath, NullLogger.Instance);
var startInfo = new ProcessStartInfo()
{
UseShellExecute = false,
FileName = System.Environment.ExpandEnvironmentVariables(@"%windir%\System32\cmd.exe"),
Arguments = "/Q",
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
WorkingDirectory = _environment.RootPath
};

if (shell.Equals("powershell", StringComparison.OrdinalIgnoreCase))
{
startInfo.FileName = System.Environment.ExpandEnvironmentVariables(@"%windir%\System32\WindowsPowerShell\v1.0\powershell.exe");
startInfo.Arguments = "-File -";
}
else
{
startInfo.FileName = System.Environment.ExpandEnvironmentVariables(@"%windir%\System32\cmd.exe");
startInfo.Arguments = "/Q";
}

foreach (var environmentVariable in exe.EnvironmentVariables)
{
startInfo.EnvironmentVariables[environmentVariable.Key] = environmentVariable.Value;
Expand Down Expand Up @@ -153,11 +164,11 @@ private void HookProcessStreamsToConnection(Process process, string connectionId
thread.Join();
}

private ProcessInfo StartProcess(string connectionId)
private ProcessInfo StartProcess(string connectionId, string shell)
{
using (_tracer.Step("start process for connectionId = " + connectionId))
{
var process = CreateProcess(connectionId);
var process = CreateProcess(connectionId, shell);
_tracer.Trace("process " + process.Id + " started");
EnsureMaxProcesses();
return new ProcessInfo(process);
Expand Down
Loading

0 comments on commit 870a005

Please sign in to comment.