Message signing implementation for legacy Windows agent
Also does a minor code refactoring to address R# warnings + add Apache 2.0 headers Change-Id: I1486909950828993400a75aa320c73adba4c086f Partial-Blueprint: message-signing
This commit is contained in:
parent
53c52f2dc9
commit
6ea51f4678
|
@ -32,14 +32,12 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,19 +1,34 @@
|
|||
using System;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Mirantis.Murano
|
||||
{
|
||||
class Command
|
||||
internal class Command
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public Dictionary<string, object> Arguments { get; set; }
|
||||
}
|
||||
class ExecutionPlan
|
||||
|
||||
internal class ExecutionPlan
|
||||
{
|
||||
public List<string> Scripts { get; set; }
|
||||
public List<Command> Commands { get; set; }
|
||||
|
@ -21,9 +36,9 @@ namespace Mirantis.Murano
|
|||
}
|
||||
|
||||
|
||||
class Program
|
||||
static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
if (args.Length < 1 || args.Length > 2)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
using System.Reflection;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" />
|
||||
</packages>
|
|
@ -1,10 +1,10 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>
|
||||
</configSections>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
|
||||
</startup>
|
||||
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<targets>
|
||||
|
@ -19,10 +19,12 @@
|
|||
<add key="rabbitmq.host" value="localhost"/>
|
||||
<add key="rabbitmq.user" value="murano"/>
|
||||
<add key="rabbitmq.password" value="murano"/>
|
||||
<add key="rabbitmq.vhost" value="murano"/>
|
||||
<add key="rabbitmq.vhost" value="/"/>
|
||||
<add key="rabbitmq.resultExchange" value=""/>
|
||||
<add key="rabbitmq.resultRoutingKey" value="-execution-results"/>
|
||||
<add key="rabbitmq.resultRoutingKey" value="dd"/>
|
||||
<add key="rabbitmq.durableMessages" value="true"/>
|
||||
|
||||
<add key="engine.key" value=""/>
|
||||
|
||||
</appSettings>
|
||||
</configuration>
|
||||
|
|
|
@ -1,12 +1,23 @@
|
|||
using System;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
class ExecutionPlan
|
||||
internal class ExecutionPlan
|
||||
{
|
||||
public class Command
|
||||
{
|
||||
|
@ -17,5 +28,7 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
public string[] Scripts { get; set; }
|
||||
public LinkedList<Command> Commands { get; set; }
|
||||
public int RebootOnCompletion { get; set; }
|
||||
|
||||
public long Stamp { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
internal class Message : IDisposable
|
||||
{
|
||||
private readonly Action ackFunc;
|
||||
|
||||
public Message(Action ackFunc)
|
||||
{
|
||||
this.ackFunc = ackFunc;
|
||||
}
|
||||
|
||||
public Message()
|
||||
{
|
||||
}
|
||||
|
||||
public string Body { get; set; }
|
||||
public string Id { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ackFunc();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using RabbitMQ.Client;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
class RabbitMqClient : IDisposable
|
||||
internal class MessageSource : IDisposable
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
private static readonly ConnectionFactory connectionFactory;
|
||||
private static readonly string queueName;
|
||||
private IConnection currentConnecton;
|
||||
private readonly SignatureVerifier signatureVerifier;
|
||||
|
||||
|
||||
static RabbitMqClient()
|
||||
static MessageSource()
|
||||
{
|
||||
var ssl = new SslOption {
|
||||
Enabled = bool.Parse(ConfigurationManager.AppSettings["rabbitmq.ssl"] ?? "false"),
|
||||
|
@ -45,49 +58,65 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
RequestedHeartbeat = 10,
|
||||
Ssl = ssl
|
||||
};
|
||||
queueName = ConfigurationManager.AppSettings["rabbitmq.inputQueue"];
|
||||
}
|
||||
|
||||
public RabbitMqClient()
|
||||
public MessageSource()
|
||||
{
|
||||
|
||||
this.signatureVerifier = new SignatureVerifier(Encoding.ASCII.GetBytes(queueName));
|
||||
}
|
||||
|
||||
public MqMessage GetMessage()
|
||||
public Message GetMessage()
|
||||
{
|
||||
var queueName = ConfigurationManager.AppSettings["rabbitmq.inputQueue"] ?? Dns.GetHostName().ToLower();
|
||||
try
|
||||
{
|
||||
IConnection connection = null;
|
||||
IConnection connection;
|
||||
lock (this)
|
||||
{
|
||||
connection = this.currentConnecton = this.currentConnecton ?? connectionFactory.CreateConnection();
|
||||
}
|
||||
var session = connection.CreateModel();
|
||||
session.BasicQos(0, 1, false);
|
||||
//session.QueueDeclare(queueName, true, false, false, null);
|
||||
var consumer = new QueueingBasicConsumer(session);
|
||||
var consumer = new QueueingBasicConsumer(session);
|
||||
var consumeTag = session.BasicConsume(queueName, false, consumer);
|
||||
var e = (RabbitMQ.Client.Events.BasicDeliverEventArgs) consumer.Queue.Dequeue();
|
||||
Action ackFunc = delegate {
|
||||
session.BasicAck(e.DeliveryTag, false);
|
||||
session.BasicCancel(consumeTag);
|
||||
session.Close();
|
||||
};
|
||||
|
||||
return new MqMessage(ackFunc) {
|
||||
Body = Encoding.UTF8.GetString(e.Body),
|
||||
Id = e.BasicProperties.MessageId
|
||||
};
|
||||
while (true)
|
||||
{
|
||||
var e = consumer.Queue.Dequeue();
|
||||
Action ackFunc = delegate
|
||||
{
|
||||
session.BasicAck(e.DeliveryTag, false);
|
||||
session.BasicCancel(consumeTag);
|
||||
session.Close();
|
||||
};
|
||||
|
||||
byte[] signature = null;
|
||||
if (e.BasicProperties.Headers.ContainsKey("signature"))
|
||||
{
|
||||
signature = (byte[]) e.BasicProperties.Headers["signature"];
|
||||
}
|
||||
|
||||
if (this.signatureVerifier.Verify(e.Body, signature))
|
||||
{
|
||||
return new Message(ackFunc) {
|
||||
Body = Encoding.UTF8.GetString(e.Body),
|
||||
Id = e.BasicProperties.MessageId,
|
||||
};
|
||||
}
|
||||
|
||||
log.Warn("Dropping message with invalid/missing signature");
|
||||
session.BasicReject(e.DeliveryTag, false);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
Dispose();
|
||||
throw;
|
||||
if (this.currentConnecton == null) return null;
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void SendResult(MqMessage message)
|
||||
public void SendResult(Message message)
|
||||
{
|
||||
var exchangeName = ConfigurationManager.AppSettings["rabbitmq.resultExchange"] ?? "";
|
||||
var resultRoutingKey = ConfigurationManager.AppSettings["rabbitmq.resultRoutingKey"] ?? "-execution-results";
|
||||
|
@ -95,22 +124,14 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
|
||||
try
|
||||
{
|
||||
IConnection connection = null;
|
||||
IConnection connection;
|
||||
lock (this)
|
||||
{
|
||||
connection = this.currentConnecton = this.currentConnecton ?? connectionFactory.CreateConnection();
|
||||
}
|
||||
var session = connection.CreateModel();
|
||||
/*if (!string.IsNullOrEmpty(resultQueue))
|
||||
{
|
||||
//session.QueueDeclare(resultQueue, true, false, false, null);
|
||||
if (!string.IsNullOrEmpty(exchangeName))
|
||||
{
|
||||
session.ExchangeBind(exchangeName, resultQueue, resultQueue);
|
||||
}
|
||||
}*/
|
||||
var basicProperties = session.CreateBasicProperties();
|
||||
basicProperties.SetPersistent(durable);
|
||||
basicProperties.Persistent = durable;
|
||||
basicProperties.MessageId = message.Id;
|
||||
basicProperties.ContentType = "application/json";
|
||||
session.BasicPublish(exchangeName, resultRoutingKey, basicProperties, Encoding.UTF8.GetBytes(message.Body));
|
||||
|
@ -129,18 +150,13 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
{
|
||||
try
|
||||
{
|
||||
if (this.currentConnecton != null)
|
||||
{
|
||||
this.currentConnecton.Close();
|
||||
}
|
||||
var connection = this.currentConnecton;
|
||||
this.currentConnecton = null;
|
||||
connection.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.currentConnecton = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
class MqMessage
|
||||
{
|
||||
private readonly Action ackFunc;
|
||||
|
||||
public MqMessage(Action ackFunc)
|
||||
{
|
||||
this.ackFunc = ackFunc;
|
||||
}
|
||||
|
||||
public MqMessage()
|
||||
{
|
||||
}
|
||||
|
||||
public string Body { get; set; }
|
||||
public string Id { get; set; }
|
||||
|
||||
public void Ack()
|
||||
{
|
||||
ackFunc();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,19 @@
|
|||
using System;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
@ -12,9 +27,10 @@ using Newtonsoft.Json;
|
|||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
class PlanExecutor
|
||||
internal class PlanExecutor
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
private long lastStamp = -1;
|
||||
|
||||
class ExecutionResult
|
||||
{
|
||||
|
@ -22,37 +38,45 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
public object Result { get; set; }
|
||||
}
|
||||
|
||||
private readonly string path;
|
||||
private readonly string baseDir;
|
||||
|
||||
public PlanExecutor(string path)
|
||||
public PlanExecutor(string baseDir)
|
||||
{
|
||||
this.path = path;
|
||||
this.baseDir = baseDir;
|
||||
}
|
||||
|
||||
public bool RebootNeeded { get; set; }
|
||||
|
||||
public void Execute()
|
||||
public void Execute(string path)
|
||||
{
|
||||
RebootNeeded = false;
|
||||
var resultPath = this.path + ".result";
|
||||
var resultPath = path + ".result";
|
||||
var tmpResultPath = resultPath + ".tmp";
|
||||
Runspace runSpace = null;
|
||||
try
|
||||
{
|
||||
var plan = JsonConvert.DeserializeObject<ExecutionPlan>(File.ReadAllText(this.path));
|
||||
List<ExecutionResult> currentResults = null;
|
||||
var plan = JsonConvert.DeserializeObject<ExecutionPlan>(File.ReadAllText(path));
|
||||
List<ExecutionResult> currentResults;
|
||||
try
|
||||
{
|
||||
currentResults = File.Exists(resultPath) ?
|
||||
JsonConvert.DeserializeObject<List<ExecutionResult>>(File.ReadAllText(resultPath)) :
|
||||
currentResults = File.Exists(tmpResultPath) ?
|
||||
JsonConvert.DeserializeObject<List<ExecutionResult>>(File.ReadAllText(tmpResultPath)) :
|
||||
new List<ExecutionResult>();
|
||||
}
|
||||
catch(Exception exception)
|
||||
{
|
||||
Log.WarnException("Cannot deserialize previous execution result", exception);
|
||||
log.Warn(exception, "Cannot deserialize previous execution result");
|
||||
currentResults = new List<ExecutionResult>();
|
||||
}
|
||||
|
||||
runSpace = RunspaceFactory.CreateRunspace();
|
||||
var lastStamp = GetLastStamp();
|
||||
if (plan.Stamp > 0 && plan.Stamp <= lastStamp)
|
||||
{
|
||||
log.Warn("Dropping old/duplicate plan");
|
||||
return;
|
||||
}
|
||||
|
||||
runSpace = RunspaceFactory.CreateRunspace();
|
||||
runSpace.Open();
|
||||
|
||||
var runSpaceInvoker = new RunspaceInvoke(runSpace);
|
||||
|
@ -63,36 +87,39 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
foreach (var script in plan.Scripts)
|
||||
{
|
||||
runSpaceInvoker.Invoke(Encoding.UTF8.GetString(Convert.FromBase64String(script)));
|
||||
Log.Debug("Loaded script #{0}", ++index);
|
||||
log.Debug("Loaded script #{0}", ++index);
|
||||
}
|
||||
}
|
||||
|
||||
while (plan.Commands != null && plan.Commands.Any())
|
||||
{
|
||||
var command = plan.Commands.First();
|
||||
Log.Debug("Preparing to execute command {0}", command.Name);
|
||||
log.Debug("Preparing to execute command {0}", command.Name);
|
||||
|
||||
var pipeline = runSpace.CreatePipeline();
|
||||
var psCommand = new Command(command.Name);
|
||||
if (command.Arguments != null)
|
||||
{
|
||||
foreach (var kvp in command.Arguments)
|
||||
{
|
||||
var value = ConvertArgument(kvp.Value);
|
||||
psCommand.Parameters.Add(kvp.Key, value);
|
||||
}
|
||||
}
|
||||
if (command.Name != null)
|
||||
{
|
||||
var psCommand = new Command(command.Name);
|
||||
if (command.Arguments != null)
|
||||
{
|
||||
foreach (var kvp in command.Arguments)
|
||||
{
|
||||
var value = ConvertArgument(kvp.Value);
|
||||
psCommand.Parameters.Add(kvp.Key, value);
|
||||
}
|
||||
}
|
||||
|
||||
Log.Info("Executing {0} {1}", command.Name, string.Join(" ",
|
||||
(command.Arguments ?? new Dictionary<string, object>()).Select(
|
||||
t => string.Format("{0}={1}", t.Key, t.Value == null ? "null" : t.Value.ToString()))));
|
||||
log.Info("Executing {0} {1}", command.Name, string.Join(" ",
|
||||
(command.Arguments ?? new Dictionary<string, object>()).Select(
|
||||
t => string.Format("{0}={1}", t.Key, t.Value?.ToString() ?? "null"))));
|
||||
|
||||
pipeline.Commands.Add(psCommand);
|
||||
pipeline.Commands.Add(psCommand);
|
||||
}
|
||||
|
||||
try
|
||||
try
|
||||
{
|
||||
var result = pipeline.Invoke();
|
||||
Log.Debug("Command {0} executed", command.Name);
|
||||
log.Debug("Command {0} executed", command.Name);
|
||||
if (result != null)
|
||||
{
|
||||
currentResults.Add(new ExecutionResult {
|
||||
|
@ -104,21 +131,18 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
catch (Exception exception)
|
||||
{
|
||||
object additionInfo = null;
|
||||
if (exception is ActionPreferenceStopException)
|
||||
{
|
||||
var apse = exception as ActionPreferenceStopException;
|
||||
if (apse.ErrorRecord != null)
|
||||
{
|
||||
additionInfo = new {
|
||||
ScriptStackTrace = apse.ErrorRecord.ScriptStackTrace,
|
||||
PositionMessage = apse.ErrorRecord.InvocationInfo.PositionMessage
|
||||
};
|
||||
exception = apse.ErrorRecord.Exception;
|
||||
}
|
||||
}
|
||||
var apse = exception as ActionPreferenceStopException;
|
||||
if (apse?.ErrorRecord != null)
|
||||
{
|
||||
additionInfo = new {
|
||||
ScriptStackTrace = apse.ErrorRecord.ScriptStackTrace,
|
||||
PositionMessage = apse.ErrorRecord.InvocationInfo.PositionMessage
|
||||
};
|
||||
exception = apse.ErrorRecord.Exception;
|
||||
}
|
||||
|
||||
|
||||
Log.WarnException("Exception while executing command " + command.Name, exception);
|
||||
log.Warn(exception, "Exception while executing command " + command.Name);
|
||||
currentResults.Add(new ExecutionResult
|
||||
{
|
||||
IsException = true,
|
||||
|
@ -132,10 +156,14 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
{
|
||||
plan.Commands.RemoveFirst();
|
||||
File.WriteAllText(path, JsonConvert.SerializeObject(plan));
|
||||
File.WriteAllText(resultPath, JsonConvert.SerializeObject(currentResults));
|
||||
File.WriteAllText(tmpResultPath, JsonConvert.SerializeObject(currentResults));
|
||||
}
|
||||
}
|
||||
runSpace.Close();
|
||||
if (plan.Stamp > 0)
|
||||
{
|
||||
SetLastStamp(plan.Stamp);
|
||||
}
|
||||
var executionResult = JsonConvert.SerializeObject(new ExecutionResult {
|
||||
IsException = false,
|
||||
Result = currentResults
|
||||
|
@ -152,11 +180,12 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
RebootNeeded = true;
|
||||
}
|
||||
}
|
||||
File.WriteAllText(resultPath, executionResult);
|
||||
File.Delete(tmpResultPath);
|
||||
File.WriteAllText(resultPath, executionResult);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Log.WarnException("Exception while processing execution plan", exception);
|
||||
log.Warn(exception, "Exception while processing execution plan");
|
||||
File.WriteAllText(resultPath, JsonConvert.SerializeObject(new ExecutionResult {
|
||||
IsException = true,
|
||||
Result = exception.Message
|
||||
|
@ -173,38 +202,33 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
catch
|
||||
{}
|
||||
}
|
||||
Log.Debug("Finished processing of execution plan");
|
||||
log.Debug("Finished processing of execution plan");
|
||||
}
|
||||
}
|
||||
|
||||
private static object ConvertArgument(object arg)
|
||||
{
|
||||
if (arg is JArray)
|
||||
switch (arg)
|
||||
{
|
||||
var array = arg as JArray;
|
||||
return array.Select(ConvertArgument).ToArray();
|
||||
case JArray array:
|
||||
return array.Select(ConvertArgument).ToArray();
|
||||
case JValue value:
|
||||
return value.Value;
|
||||
case JObject dict:
|
||||
var result = new Hashtable();
|
||||
foreach (var item in dict)
|
||||
{
|
||||
result.Add(item.Key, ConvertArgument(item.Value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (arg is JValue)
|
||||
{
|
||||
var value = (JValue) arg;
|
||||
return value.Value;
|
||||
}
|
||||
else if (arg is JObject)
|
||||
{
|
||||
var dict = (JObject)arg;
|
||||
var result = new Hashtable();
|
||||
foreach (var item in dict)
|
||||
{
|
||||
result.Add(item.Key, ConvertArgument(item.Value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return arg;
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
private static object SerializePsObject(PSObject obj)
|
||||
{
|
||||
if (obj.BaseObject is PSCustomObject)
|
||||
if (obj.BaseObject is PSCustomObject)
|
||||
{
|
||||
var result = new Dictionary<string, object>();
|
||||
foreach (var property in obj.Properties.Where(p => p.IsGettable))
|
||||
|
@ -219,15 +243,61 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
}
|
||||
return result;
|
||||
}
|
||||
else if (obj.BaseObject is IEnumerable<PSObject>)
|
||||
{
|
||||
return ((IEnumerable<PSObject>) obj.BaseObject).Select(SerializePsObject).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
return obj.BaseObject;
|
||||
}
|
||||
|
||||
if (obj.BaseObject is IEnumerable<PSObject> objects)
|
||||
{
|
||||
return objects.Select(SerializePsObject).ToArray();
|
||||
}
|
||||
|
||||
return obj.BaseObject;
|
||||
}
|
||||
|
||||
private long GetLastStamp()
|
||||
{
|
||||
if (this.lastStamp >= 0)
|
||||
{
|
||||
return this.lastStamp;
|
||||
}
|
||||
|
||||
var path = Path.Combine(this.baseDir, "stamp.txt");
|
||||
if (File.Exists(path))
|
||||
{
|
||||
try
|
||||
{
|
||||
var stampData = File.ReadAllText(path);
|
||||
this.lastStamp = long.Parse(stampData);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.lastStamp = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.lastStamp = 0;
|
||||
}
|
||||
|
||||
return this.lastStamp;
|
||||
}
|
||||
|
||||
private void SetLastStamp(long value)
|
||||
{
|
||||
var path = Path.Combine(this.baseDir, "stamp.txt");
|
||||
try
|
||||
{
|
||||
File.WriteAllText(path, value.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
log.Error(e, "Cannot persist last stamp");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.lastStamp = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,55 +1,69 @@
|
|||
using System;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management.Automation;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using NLog;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
[DisplayName("Murano Agent")]
|
||||
sealed public class Program : WindowsService
|
||||
public sealed class Program : WindowsService
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
private volatile bool stop;
|
||||
private Thread thread;
|
||||
private RabbitMqClient rabbitMqClient;
|
||||
private MessageSource messageSource;
|
||||
private int delayFactor = 1;
|
||||
private string plansDir;
|
||||
|
||||
static void Main(string[] args)
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Start(new Program(), args);
|
||||
Start(new Program(), args);
|
||||
}
|
||||
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
base.OnStart(args);
|
||||
base.OnStart(args);
|
||||
|
||||
Log.Info("Version 0.5.4");
|
||||
log.Info("Version 0.6");
|
||||
|
||||
this.rabbitMqClient = new RabbitMqClient();
|
||||
this.messageSource = new MessageSource();
|
||||
|
||||
var basePath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
|
||||
this.plansDir = Path.Combine(basePath, "plans");
|
||||
|
||||
|
||||
if (!Directory.Exists(plansDir))
|
||||
{
|
||||
Directory.CreateDirectory(plansDir);
|
||||
Directory.CreateDirectory(plansDir);
|
||||
}
|
||||
|
||||
this.thread = new Thread(Loop);
|
||||
this.thread.Start();
|
||||
}
|
||||
|
||||
void Loop()
|
||||
private void Loop()
|
||||
{
|
||||
const string unknownName = "unknown";
|
||||
const string unknownName = "unknown";
|
||||
var executor = new PlanExecutor(this.plansDir);
|
||||
while (!stop)
|
||||
{
|
||||
try
|
||||
|
@ -57,40 +71,45 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
foreach (var file in Directory.GetFiles(this.plansDir, "*.json.result")
|
||||
.Where(file => !File.Exists(Path.Combine(this.plansDir, Path.GetFileNameWithoutExtension(file)))))
|
||||
{
|
||||
var id = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file)) ?? unknownName;
|
||||
var id = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(file));
|
||||
if (id.Equals(unknownName, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
id = "";
|
||||
}
|
||||
|
||||
var result = File.ReadAllText(file);
|
||||
Log.Info("Sending results for {0}", id ?? unknownName);
|
||||
rabbitMqClient.SendResult(new MqMessage { Body = result, Id = id });
|
||||
log.Info("Sending results for {0}", id);
|
||||
messageSource.SendResult(new Message { Body = result, Id = id });
|
||||
File.Delete(file);
|
||||
}
|
||||
|
||||
var path = Directory.EnumerateFiles(this.plansDir, "*.json").FirstOrDefault();
|
||||
if (path == null)
|
||||
{
|
||||
var message = rabbitMqClient.GetMessage();
|
||||
var id = message.Id;
|
||||
if(string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = unknownName;
|
||||
}
|
||||
|
||||
path = Path.Combine(this.plansDir, string.Format("{0}.json", id));
|
||||
File.WriteAllText(path, message.Body);
|
||||
Log.Info("Received new execution plan {0}", id);
|
||||
message.Ack();
|
||||
using (var message = messageSource.GetMessage())
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var id = message.Id;
|
||||
if (string.IsNullOrEmpty(id))
|
||||
{
|
||||
id = unknownName;
|
||||
}
|
||||
|
||||
path = Path.Combine(this.plansDir, string.Format("{0}.json", id));
|
||||
File.WriteAllText(path, message.Body);
|
||||
log.Info("Received new execution plan {0}", id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var id = Path.GetFileNameWithoutExtension(path);
|
||||
Log.Info("Executing exising plan {0}", id);
|
||||
log.Info("Executing exising plan {0}", id);
|
||||
}
|
||||
var executor = new PlanExecutor(path);
|
||||
executor.Execute();
|
||||
|
||||
executor.Execute(path);
|
||||
File.Delete(path);
|
||||
delayFactor = 1;
|
||||
|
||||
|
@ -104,23 +123,13 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
{
|
||||
WaitOnException(exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void Reboot()
|
||||
{
|
||||
Log.Info("Going for reboot!!");
|
||||
log.Info("Going for reboot!!");
|
||||
LogManager.Flush();
|
||||
/*try
|
||||
{
|
||||
System.Diagnostics.Process.Start("shutdown.exe", "-r -t 0");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException("Cannot execute shutdown.exe", ex);
|
||||
}*/
|
||||
|
||||
|
||||
try
|
||||
|
@ -130,24 +139,23 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
catch (Exception exception)
|
||||
{
|
||||
|
||||
Log.FatalException("Reboot exception", exception);
|
||||
log.Fatal(exception, "Reboot exception");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.Info("Waiting for reboot");
|
||||
log.Info("Waiting for reboot");
|
||||
for (var i = 0; i < 10 * 60 * 5 && !stop; i++)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
Log.Info("Done waiting for reboot");
|
||||
log.Info("Done waiting for reboot");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void WaitOnException(Exception exception)
|
||||
{
|
||||
if (stop) return;
|
||||
Log.WarnException("Exception in main loop", exception);
|
||||
log.Warn(exception, "Exception in main loop");
|
||||
var i = 0;
|
||||
while (!stop && i < 10 * (delayFactor * delayFactor))
|
||||
{
|
||||
|
@ -160,9 +168,8 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
protected override void OnStop()
|
||||
{
|
||||
stop = true;
|
||||
this.rabbitMqClient.Dispose();
|
||||
this.messageSource.Dispose();
|
||||
base.OnStop();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
using System.Reflection;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
using System;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.Configuration.Install;
|
||||
using System.Reflection;
|
||||
using System.ServiceProcess;
|
||||
|
@ -15,7 +30,7 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
this.serviceName = serviceName;
|
||||
}
|
||||
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public bool Restart(string[] args, TimeSpan timeout)
|
||||
{
|
||||
|
@ -26,7 +41,7 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
|
||||
service.Stop();
|
||||
service.WaitForStatus(ServiceControllerStatus.Stopped, timeout);
|
||||
Log.Info("Service is stopped");
|
||||
log.Info("Service is stopped");
|
||||
|
||||
// count the rest of the timeout
|
||||
var millisec2 = TimeSpan.FromMilliseconds(Environment.TickCount);
|
||||
|
@ -34,12 +49,12 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
|
||||
service.Start(args);
|
||||
service.WaitForStatus(ServiceControllerStatus.Running, timeout);
|
||||
Log.Info("Service has started");
|
||||
log.Info("Service has started");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException("Cannot restart service " + serviceName, ex);
|
||||
log.Error(ex, "Cannot restart service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +70,7 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException("Cannot stop service " + serviceName, ex);
|
||||
log.Error(ex, "Cannot stop service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +86,7 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException("Cannot start service " + serviceName, ex);
|
||||
log.Error(ex, "Cannot start service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -81,11 +96,11 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
try
|
||||
{
|
||||
ManagedInstallerClass.InstallHelper(
|
||||
new string[] { Assembly.GetEntryAssembly().Location });
|
||||
new[] { Assembly.GetEntryAssembly().Location });
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Log.ErrorException("Cannot install service " + serviceName, ex);
|
||||
log.Error(ex, "Cannot install service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -96,11 +111,11 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
try
|
||||
{
|
||||
ManagedInstallerClass.InstallHelper(
|
||||
new string[] { "/u", Assembly.GetEntryAssembly().Location });
|
||||
new[] { "/u", Assembly.GetEntryAssembly().Location });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.ErrorException("Cannot uninstall service " + serviceName, ex);
|
||||
log.Error(ex, "Cannot uninstall service " + serviceName);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace Mirantis.Murano.WindowsAgent
|
||||
{
|
||||
internal class SignatureVerifier
|
||||
{
|
||||
private readonly ISigner signer;
|
||||
private readonly byte[] salt;
|
||||
|
||||
public SignatureVerifier(byte[] salt)
|
||||
{
|
||||
var keyStr = ConfigurationManager.AppSettings["engine.key"];
|
||||
if (string.IsNullOrEmpty(keyStr)) return;
|
||||
|
||||
var reader = new StringReader(keyStr);
|
||||
var key = (RsaKeyParameters) new PemReader(reader).ReadObject();
|
||||
this.signer = SignerUtilities.GetSigner("SHA256withRSA");
|
||||
this.signer.Init(false, key);
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
public bool Verify(byte[] data, byte[] signature)
|
||||
{
|
||||
if (this.signer == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (signature == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.signer.Reset();
|
||||
this.signer.BlockUpdate(this.salt, 0, this.salt.Length);
|
||||
this.signer.BlockUpdate(data, 0, data.Length);
|
||||
return this.signer.VerifySignature(signature);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
@ -9,9 +9,10 @@
|
|||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Mirantis.Murano.WindowsAgent</RootNamespace>
|
||||
<AssemblyName>WindowsAgent</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
|
||||
<TargetFrameworkProfile>
|
||||
</TargetFrameworkProfile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
|
@ -34,15 +35,22 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>
|
||||
</StartupObject>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
|
||||
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
|
||||
<HintPath>..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog">
|
||||
<HintPath>..\packages\NLog.2.0.0.2000\lib\net40\NLog.dll</HintPath>
|
||||
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RabbitMQ.Client">
|
||||
<HintPath>..\packages\RabbitMQ.Client.3.0.2\lib\net30\RabbitMQ.Client.dll</HintPath>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NLog.4.4.12\lib\net45\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RabbitMQ.Client, Version=3.6.9.0, Culture=neutral, PublicKeyToken=89e7d7c5feba84ce, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\RabbitMQ.Client.3.6.9\lib\net45\RabbitMQ.Client.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
|
@ -53,22 +61,18 @@
|
|||
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ExecutionPlan.cs" />
|
||||
<Compile Include="MqMessage.cs" />
|
||||
<Compile Include="Message.cs" />
|
||||
<Compile Include="PlanExecutor.cs" />
|
||||
<Compile Include="Program.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RabbitMqClient.cs" />
|
||||
<Compile Include="MessageSource.cs" />
|
||||
<Compile Include="ServiceManager.cs" />
|
||||
<Compile Include="SignatureVerifier.cs" />
|
||||
<Compile Include="WindowsService.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
|
@ -77,7 +81,9 @@
|
|||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="App.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="packages.config" />
|
||||
<None Include="SampleExecutionPlan.json" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
using System;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
@ -10,8 +25,7 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
{
|
||||
public abstract class WindowsService : ServiceBase
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
public bool RunningAsService { get; private set; }
|
||||
private static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
protected static void Start(WindowsService service, string[] arguments)
|
||||
{
|
||||
|
@ -39,14 +53,12 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
}
|
||||
else if (!arguments.Contains("/console", StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
service.RunningAsService = true;
|
||||
Run(service);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
service.RunningAsService = false;
|
||||
Console.Title = service.ServiceName;
|
||||
service.OnStart(Environment.GetCommandLineArgs());
|
||||
service.WaitForExitSignal();
|
||||
|
@ -81,14 +93,14 @@ namespace Mirantis.Murano.WindowsAgent
|
|||
|
||||
protected override void OnStart(string[] args)
|
||||
{
|
||||
Log.Info("Service {0} started", ServiceName);
|
||||
log.Info("Service {0} started", ServiceName);
|
||||
|
||||
base.OnStart(args);
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
Log.Info("Service {0} exited", ServiceName);
|
||||
log.Info("Service {0} exited", ServiceName);
|
||||
base.OnStop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
using System.ComponentModel;
|
||||
// Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
// contributor license agreements. See the NOTICE file distributed with
|
||||
// this work for additional information regarding copyright ownership.
|
||||
// The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
// (the "License"); you may not use this file except in compliance with
|
||||
// the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Configuration.Install;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
|
||||
<package id="NLog" version="2.0.0.2000" targetFramework="net45" />
|
||||
<package id="RabbitMQ.Client" version="3.0.2" targetFramework="net45" />
|
||||
<package id="BouncyCastle" version="1.8.1" targetFramework="net451" />
|
||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net45" />
|
||||
<package id="NLog" version="4.4.12" targetFramework="net45" />
|
||||
<package id="RabbitMQ.Client" version="3.6.9" targetFramework="net45" />
|
||||
</packages>
|
Loading…
Reference in New Issue