Introduce the Download#FailOnError option. (#195)

* Introduce the Download#FailOnError option.

The change also adds logging of download operations to the wrapper log

* Add documentation for the failOnError flag
pull/207/head
Oleg Nenashev 2017-04-11 22:57:39 +02:00 committed by GitHub
parent 5803d3ce15
commit f0770a0e15
6 changed files with 111 additions and 6 deletions

View File

@ -159,12 +159,20 @@ For servers requiring authentication some parameters must be specified depending
The parameter “unsecureAuth” is only effective when the transfer protocol is HTTP - unencrypted data transfer. This is a security vulnerability because the credentials are send in clear text! For a SSPI authentication this is not relevant because the authentication tokens are encrypted.
For target servers using the HTTPS transfer protocol it is necessary, that the CA which issued the server certificate is trusted by the client. This is normally the situation when the server ist located in the Internet. When an organisation is using a self issued CA for the intranet this probably is not the case. In this case it is necessary to import the CA to the Certificate MMC of the Windows client. Have a look to the instructions on this [site](https://msdn.microsoft.com/de-de/library/system.net.credentialcache.defaultcredentials(v=vs.85).aspx). The self issued CA must be imported to the Trusted Root Certification Authorities for the computer.
```
By default, the `download` command does not fail the service startup if the operation fails (e.g. `from` is not available).
In order to force the download failure in such case, it is possible to specify the `failOnError` boolean attribute.
Examples:
```xml
<download from="http://example.com/some.dat" to="%BASE%\some.dat" />
<download from="http://example.com/some.dat" to="%BASE%\some.dat" failOnError="true"/>
<download from="https://example.com/some.dat" to="%BASE%\some.dat" auth="sspi" />
<download from="https://example.com/some.dat" to="%BASE%\some.dat"
<download from="https://example.com/some.dat" to="%BASE%\some.dat" failOnError="true"
auth="basic" username="aUser" password="aPassw0rd" />
<download from="http://example.com/some.dat" to="%BASE%\some.dat"

View File

@ -204,16 +204,25 @@ namespace winsw
// handle downloads
foreach (Download d in _descriptor.Downloads)
{
LogEvent("Downloading: " + d.From+ " to "+d.To);
String downloadMsg = "Downloading: " + d.From + " to " + d.To + ". failOnError=" + d.FailOnError;
LogEvent(downloadMsg);
Log.Info(downloadMsg);
try
{
d.Perform();
}
catch (Exception e)
{
LogEvent("Failed to download " + d.From + " to " + d.To + "\n" + e.Message);
Log.Error("Failed to download " + d.From +" to "+d.To, e);
// but just keep going
string errorMessage = "Failed to download " + d.From + " to " + d.To;
LogEvent(errorMessage + ". " + e.Message);
Log.Error(errorMessage, e);
// TODO: move this code into the download logic
if (d.FailOnError)
{
throw new IOException(errorMessage, e);
}
// Else just keep going
}
}

26
src/Core/WinSWCore/Download.cs Normal file → Executable file
View File

@ -20,11 +20,22 @@ namespace winsw
public readonly string Username;
public readonly string Password;
public readonly bool UnsecureAuth = false;
public readonly bool FailOnError;
public Download(string from, string to, bool failOnError = false)
{
From = from;
To = to;
FailOnError = failOnError;
}
internal Download(XmlNode n)
{
From = Environment.ExpandEnvironmentVariables(n.Attributes["from"].Value);
To = Environment.ExpandEnvironmentVariables(n.Attributes["to"].Value);
var failOnErrorNode = n.Attributes["failOnError"];
FailOnError = failOnErrorNode != null ? Boolean.Parse(failOnErrorNode.Value) : false;
string tmpStr = "";
try
@ -80,6 +91,12 @@ namespace winsw
request.Headers["Authorization"] = "Basic " + authInfo;
}
/// <summary>
/// Downloads the requested file and puts it to the specified target.
/// </summary>
/// <exception cref="System.Net.WebException">
/// Download failure. FailOnError flag should be processed outside.
/// </exception>
public void Perform()
{
WebRequest req = WebRequest.Create(From);
@ -106,6 +123,15 @@ namespace winsw
File.Move(To + ".tmp", To);
}
/// <summary>
/// Produces the XML configuuration entry.
/// </summary>
/// <returns>XML String for the configuration file</returns>
public String toXMLConfig()
{
return "<download from=\"" + From + "\" to=\"" + To + "\" failOnError=\"" + FailOnError + "\"/>";
}
private static void CopyStream(Stream i, Stream o)
{
byte[] buf = new byte[8192];

View File

@ -0,0 +1,56 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Text;
using winsw;
using winswTests.Util;
namespace winswTests
{
[TestFixture]
class DownloadTest
{
private const string From = "http://www.nosuchhostexists.foo.myorg/foo.xml";
private const string To = "%BASE%\\foo.xml";
/// <summary>
/// Ensures that the fail-on-error field is being processed correctly.
/// </summary>
[TestCase(true)]
[TestCase(false)]
public void Download_FailOnError(bool failOnError)
{
Download d = new Download(From, To, failOnError);
var sd = ConfigXmlBuilder.create()
.WithDownload(d)
.ToServiceDescriptor(true);
var loaded = getSingleEntry(sd);
Assert.That(loaded.From, Is.EqualTo(From));
Assert.That(loaded.To, Is.EqualTo(To));
Assert.That(loaded.FailOnError, Is.EqualTo(failOnError), "Unexpected FailOnError value");
}
/// <summary>
/// Ensures that the fail-on-error field is being processed correctly.
/// </summary>
[Test]
public void Download_FailOnError_Undefined()
{
var sd = ConfigXmlBuilder.create()
.WithRawEntry("<download from=\"http://www.nosuchhostexists.foo.myorg/foo.xml\" to=\"%BASE%\\foo.xml\"/>")
.ToServiceDescriptor(true);
var loaded = getSingleEntry(sd);
Assert.That(loaded.FailOnError, Is.False);
}
private Download getSingleEntry(ServiceDescriptor sd)
{
var downloads = sd.Downloads.ToArray();
Assert.That(downloads.Length, Is.EqualTo(1), "Service Descriptor is expected to have only one entry");
return downloads[0];
}
}
}

View File

@ -119,5 +119,10 @@ namespace winswTests.Util
return this;
}
public ConfigXmlBuilder WithDownload(Download download)
{
return WithRawEntry(download.toXMLConfig());
}
}
}

View File

@ -56,6 +56,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Configuration\ExamplesTest.cs" />
<Compile Include="DownloadTest.cs" />
<Compile Include="Extensions\ExtensionTestBase.cs" />
<Compile Include="Extensions\RunawayProcessKillerTest.cs" />
<Compile Include="Extensions\SharedDirectoryMapperTest.cs" />