Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display agent success / error messages in UI #735

Merged
merged 5 commits into from
Apr 7, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/app/Fake.Deploy.Lib/Fake.Deploy.Lib.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<Compile Include="AssemblyInfo.fs" />
<Compile Include="SshRsaModule.fs" />
<Compile Include="Json.fs" />
<Compile Include="HttpHeaderHelper.fs" />
<Compile Include="HttpListenerHelper.fs" />
<Compile Include="FakeDeployAgentHelper.fs" />
<None Include="app.config" />
Expand Down
2 changes: 1 addition & 1 deletion src/app/Fake.Deploy.Lib/FakeDeployAgentHelper.fs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ let uploadData (action: Action) (url: Url) (body: byte[]) =

let uploadFile (action: Action) (url: Url) (file: FilePath) (args: string[]) =
let req = webRequest url action
req.Headers.Add(scriptArgumentsHeaderName, String.Join (";", args))
req.Headers.Add(scriptArgumentsHeaderName, args |> toHeaderValue)
req.AllowWriteStreamBuffering <- false
use fileStream = File.OpenRead file
req.ContentLength <- fileStream.Length
Expand Down
34 changes: 34 additions & 0 deletions src/app/Fake.Deploy.Lib/HttpHeaderHelper.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[<AutoOpen>]
module Fake.HttpHeaderHelper
open System
open System.Text.RegularExpressions

let toHeaderValue (values:string []) : string =
values
|> Array.map (fun x ->
x.Replace("\"", "%22")
|> sprintf "\"%s\""
)
|> fun strs -> System.String.Join(",", strs
)

let private regex = Regex("(\"[^\"]*\")(?:,(\"[^\"]*\"))*", RegexOptions.Compiled)
let fromHeaderValue (value:string) : string [] =
let matches = regex.Matches(value)
//back compat: existing agents not expecting quoted params will continue to function.
if matches.Count = 0 then [|value|]
else
matches |> Seq.cast
|> Seq.collect (fun (m:Match) -> m.Groups |> Seq.cast)
|> Seq.skip 1
|> Seq.collect (fun (g:Group) ->
g.Captures |> Seq.cast |> Seq.map (fun (c:Capture) -> c.Value)
|> Seq.map (fun (x:string) ->
x.Substring(1, x.Length - 2)
|> fun y -> y.Replace("%22", "\"")
)
)
|> Array.ofSeq



7 changes: 4 additions & 3 deletions src/app/Fake.Deploy/DeploymentAgent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ let getBodyFromNancyRequest (ctx : Nancy.Request) =

let getScriptArgumentsFromNancyRequest (ctx : Nancy.Request) =
ctx.Headers
|> Seq.choose (fun pair -> if pair.Key = FakeDeployAgentHelper.scriptArgumentsHeaderName then Some pair.Value else None)
|> Seq.concat
|> Seq.toArray
|> Seq.choose (fun pair -> if pair.Key = FakeDeployAgentHelper.scriptArgumentsHeaderName then Some <| pair.Value else None)
|> Seq.head
|> Seq.head
|> fromHeaderValue

let runDeployment workDir (ctx : Nancy.Request) =
let packageBytes = getBodyFromNancyRequest ctx
Expand Down
2 changes: 1 addition & 1 deletion src/deploy.web/Fake.Deploy.Web/Modules/Api.Agent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ open Fake.Deploy.Web.Module.NancyOp

type AgentResponse<'t> =
{ case : string
values : 't [] }
fields : 't [] }

type ApiAgent(dataProvider : IDataProvider, agentProxy : AgentProxy) as http =
inherit FakeModule("/api/v1/agent")
Expand Down
21 changes: 8 additions & 13 deletions src/deploy.web/Fake.Deploy.Web/Modules/Api.Package.fs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,6 @@ type ApiPackage (dataProvider : IDataProvider) as http =

let packageTemp = Path.Combine(appdata.FullName, "Package_Temp")

let deploy packageFileName agent =
let toStr (msgs:seq<ConsoleMessage>) =
msgs |> Seq.map(fun msg -> sprintf "%s: %s" (msg.Timestamp.ToString("yyyy-MM-dd HH:mm:ss")) msg.Message )

let url = Uri(agent.Address, "/fake/")
let response = postDeploymentPackage url.AbsoluteUri packageFileName [||]
match response with
| Failure x -> { Agent = agent.Name; Messages = toStr x.Messages; Success = not <| x.IsError; Error = x.Exception.ToString() }
| Success x -> { Agent = agent.Name; Messages = toStr x.Messages; Success = not <| x.IsError; Error = "" }
| _ -> { Agent = agent.Name; Messages = []; Success = false; Error = "Unexpected response from agent" }

do
http.post "/rollback" (fun p ->
let body = http.Bind<ApiModels.RollbackRequest>()
Expand All @@ -58,6 +47,12 @@ type ApiPackage (dataProvider : IDataProvider) as http =
let agentId = http.Request.Form ?> "agentId"
let agent = dataProvider.GetAgents [agentId] |> Seq.head
let url = agent.Address.AbsoluteUri + "fake/"
let env =
[agent.EnvironmentId]
|>dataProvider.GetEnvironments
|> Seq.head
|> fun x -> x.Name
|> sprintf "env=%s"
Directory.CreateDirectory(packageTemp) |> ignore
let files =
http.Request.Files
Expand All @@ -69,9 +64,9 @@ type ApiPackage (dataProvider : IDataProvider) as http =
let code, message =
files
|> Seq.map(fun file ->
match postDeploymentPackage url file [||] with
match postDeploymentPackage url file [|env|] with
| Failure(err) ->
file, Some err, HttpStatusCode.InternalServerError, None
file, Some err, HttpStatusCode.InternalServerError, Some(err)
| Success a ->
if File.Exists(file) then File.Delete(file)
file, None, HttpStatusCode.Created, Some a
Expand Down
30 changes: 16 additions & 14 deletions src/deploy.web/Fake.Deploy.Web/Scripts/app/Home.Agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ function AgentViewModel() {

self.recentMessages = ko.observableArray();

self.updateMessagesFrom = function(data) {
self.recentMessages([]);
if (data != null) {
var response = data.xhr().response;
var messages = [];
if (response) messages = $.parseJSON(response);
$.each(messages, function (i, msg) {
self.recentMessages.push(msg);
});
}
};

self.getAgentDetails = function () {
$.ajax({
type: 'GET',
Expand All @@ -46,7 +58,8 @@ function AgentViewModel() {
contentType: 'application/json'
}).done(function (data) {
self.deployments([]);
$.each(data.values, function (i, d) {

$.each(data.fields || [], function (i, d) {
$.each(d, function (i2, dep) {
var deployment = ko.mapping.fromJS(dep);
self.deployments.push(deployment);
Expand Down Expand Up @@ -97,26 +110,15 @@ function AgentViewModel() {
$('#filePlaceHolder').modal('hide');
$('#selectPackageBtn').removeClass('hide');
toastr.info('Package deployed');
self.recentMessages([]);
if (data != null && data.result !== undefined) {
$.each(data.result, function (i, msg) {
self.recentMessages.push(msg);
});
}

self.updateMessagesFrom(data);
self.refreshDeploymentsForAgent();
},
fail: function (e, data) {
$('#fileList').html('');
$('#selectPackageBtn').removeClass('hide');
$('#filePlaceHolder').modal('hide');
toastr.error('Package deployment failed');
self.recentMessages([]);
if (data != null && data.result !== undefined) {
$.each(data.result, function (i, msg) {
self.recentMessages.push(msg);
});
}
self.updateMessagesFrom(data);
}
});

Expand Down
44 changes: 44 additions & 0 deletions src/test/Test.Fake.Deploy/Http/HttpHeaderParsingSpecs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using Fake;
using Machine.Specifications;

namespace Test.Fake.Deploy.Http
{
public class when_parsing_values_without_commas
{
private static string[] _results;

private Because of = () => _results = HttpHeaderHelper.fromHeaderValue("\"param%22one\",\"second\"");

private It should_parse_params_with_quotes = () =>
_results[0].ShouldEqual("param\"one");
private It should_parse_params_without_quotes = () =>
_results[1].ShouldEqual("second");
}


public class when_parsing_values_with_commas
{
private static string[] _results;

private Because of = () => _results = HttpHeaderHelper.fromHeaderValue("\"param,%22one\",\"second\",\"another, %22 parameter\"");

private It should_parse_params_with_comma_and_quotes = () =>
_results[0].ShouldEqual("param,\"one");
private It should_parse_params_without_quotes = () =>
_results[1].ShouldEqual("second");
private It should_parse_third_param = () =>
_results[2].ShouldEqual("another, \" parameter");
}

public class when_parsing_unquoted_params
{
private static string[] _results;

private Because of = () => _results = HttpHeaderHelper.fromHeaderValue("simple command");

private It should_parse_exact_value = () =>
_results[0].ShouldEqual("simple command");
}


}
1 change: 1 addition & 0 deletions src/test/Test.Fake.Deploy/Test.Fake.Deploy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
<Compile Include="..\Test.FAKECore\Extensions.cs">
<Link>Extensions.cs</Link>
</Compile>
<Compile Include="Http\HttpHeaderParsingSpecs.cs" />
<Compile Include="PackageMgt\ReleaseDiscoveringSpecs.cs" />
<Compile Include="PackageMgt\RollbackSpecs.cs" />
<Compile Include="PackageMgt\RoutingSpecs.cs" />
Expand Down