Changeset 17013 for branches/2924_DotNetCoreMigration/HeuristicLab.PluginInfrastructure/3.3/Isolation
- Timestamp:
- 06/17/19 10:40:41 (6 years ago)
- Location:
- branches/2924_DotNetCoreMigration/HeuristicLab.PluginInfrastructure/3.3/Isolation
- Files:
-
- 1 added
- 1 deleted
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2924_DotNetCoreMigration/HeuristicLab.PluginInfrastructure/3.3/Isolation/ApplicationRunner.cs
r16993 r17013 1 using System; 2 using System.IO; 1 using HEAL.Attic; 3 2 4 3 namespace HeuristicLab.PluginInfrastructure { 5 [S erializable]4 [StorableType("612F98AF-E254-4C5E-BD41-75B4F1D9B96D")] 6 5 public class ApplicationRunner : Runner { 7 6 /// <summary> 8 7 /// Arguments for the StartApplication. 9 8 /// </summary> 9 [Storable] 10 10 public ICommandLineArgument[] Args { get; set; } 11 11 … … 15 15 public IApplication StartApplication { 16 16 get { 17 if (application == null) {18 using (var memStream = new MemoryStream(serializedStartApplication)) {19 application = (IApplication) formatter.Deserialize(memStream);20 }17 lock (locker) { 18 if (application == null) 19 application = (IApplication)new ProtoBufSerializer().Deserialize(serializedStartApplication); 20 return application; 21 21 } 22 return application;23 22 } 24 23 set { 25 using (var ms = new MemoryStream()) {26 formatter.Serialize(ms,value);27 serializedStartApplication = ms.ToArray();24 lock (locker) { 25 serializedStartApplication = new ProtoBufSerializer().Serialize(value); 26 application = value; 28 27 } 29 28 } … … 31 30 // Encapsulated application is necessary, because it is not possible to 32 31 // instantly deserialize the application, before all assemblies are loaded. 32 [Storable] 33 33 private byte[] serializedStartApplication = new byte[0]; 34 34 35 // cache application to prevent new instances every get call of StartApplication 35 36 private IApplication application; 37 private object locker = new object(); 36 38 37 39 protected override void Execute() { … … 39 41 } 40 42 41 protected override void OnRunnerJob(RunnerJob runnerJob) { 42 switch (runnerJob) { 43 case RunnerJob.Cancel: StartApplication.OnCancel(); break; 44 case RunnerJob.Pause: StartApplication.OnPause(); break; 45 case RunnerJob.Resume: StartApplication.OnResume(); break; 46 } 43 protected override void OnRunnerMessage(RunnerMessage message) { 44 if (message is PauseRunnerMessage) 45 StartApplication.OnPause(); 46 else if (message is ResumeRunnerMessage) 47 StartApplication.OnResume(); 48 else if (message is CancelRunnerMessage) 49 StartApplication.OnCancel(); 47 50 } 48 51 } -
branches/2924_DotNetCoreMigration/HeuristicLab.PluginInfrastructure/3.3/Isolation/AssemblyInfo.cs
r16984 r17013 1 using System;1 using HEAL.Attic; 2 2 3 3 namespace HeuristicLab.PluginInfrastructure { 4 [S erializable]4 [StorableType(StorableMemberSelection.AllProperties, "D1DC9C3E-16B7-464E-A377-35FA614195AD")] 5 5 public class AssemblyInfo { 6 6 /// <summary> -
branches/2924_DotNetCoreMigration/HeuristicLab.PluginInfrastructure/3.3/Isolation/DockerRunnerHost.cs
r16993 r17013 1 1 using System.Diagnostics; 2 using System.IO; 3 using System.Threading.Tasks; 2 4 using HeuristicLab.PluginInfrastructure.Exceptions; 3 5 … … 8 10 public class DockerRunnerHost : RunnerHost { 9 11 #region Constants 10 private const string Docker = "docker"; 11 private const string ContainerStartup = "container run -i --rm "; 12 private const string Mounting = @"--mount type=bind,source=/c/Users/,target=/Users "; 13 private const string Image = "heuristiclab33:latest"; 14 private const string DockerExceptionMessage = "Docker is not running or image '" + Image + "' does not exists!"; 12 private readonly static string Docker = "docker"; 13 private readonly static string ContainerStartup = "container run -i --rm "; 14 private readonly static string MountingLinux = @"--mount type=bind,source=/c/Users/,target=/Users "; 15 private readonly static string MountingWindows = @"--mount type=bind,source=C:\Users\,target=C:\Users\ "; 16 private readonly static string LinuxImage = "." + Path.DirectorySeparatorChar + "DockerLinuxBuild" + Path.DirectorySeparatorChar + "Dockerfile"; 17 private readonly static string WindowsImage = "." + Path.DirectorySeparatorChar + "DockerWindowsBuild" + Path.DirectorySeparatorChar + "Dockerfile"; 18 private readonly static string Image = "heuristiclab33:latest"; 19 private readonly static string DockerExceptionMessage = "Docker is not running!"; 15 20 #endregion 16 21 17 22 #region Constructors 18 public DockerRunnerHost(bool doDockerAvailableCheck = true) 19 : base(Docker, ContainerStartup + Mounting + Image, null, null, null) { 20 if (doDockerAvailableCheck && !IsDockerAvailable()) 21 throw new DockerException(DockerExceptionMessage); 23 public DockerRunnerHost() 24 : base(Docker, ContainerStartup + GetTargetOSMounting() + Image, null, null, null) { 22 25 } 23 26 #endregion 24 27 25 28 #region Helper 26 private bool IsDockerAvailable() { 29 30 private static string GetTargetOSMounting() { 31 Task<bool> win = Task.Run(() => BuildImage(WindowsImage)); 32 Task<bool> lin = Task.Run(() => BuildImage(LinuxImage)); 33 if (win.Result) return MountingWindows; 34 else if (lin.Result) return MountingLinux; 35 else throw new DockerException(DockerExceptionMessage); 36 } 37 38 private static bool BuildImage(string pathToDockerfile) { 27 39 var process = new Process { 28 40 StartInfo = new ProcessStartInfo { 29 41 FileName = Docker, 30 Arguments = "image inspect " + Image, 42 Arguments = "image build -t " + Image + 43 " -f " + Path.GetFullPath(pathToDockerfile) + 44 " .", 31 45 UseShellExecute = false, 32 46 RedirectStandardOutput = true, … … 37 51 EnableRaisingEvents = false 38 52 }; 39 40 53 process.Start(); 41 54 process.BeginOutputReadLine(); -
branches/2924_DotNetCoreMigration/HeuristicLab.PluginInfrastructure/3.3/Isolation/Runner.cs
r16993 r17013 2 2 using System.Collections.Generic; 3 3 using System.IO; 4 using System.Runtime.Serialization;5 using System.Runtime.Serialization.Formatters.Binary;6 4 using System.Threading; 5 using System.Threading.Tasks; 6 using HEAL.Attic; 7 7 8 8 namespace HeuristicLab.PluginInfrastructure { 9 [S erializable]9 [StorableType(StorableMemberSelection.MarkedOnly, "D93CBE04-9847-417A-AAB5-7FBCA6A32247")] 10 10 public abstract class Runner : IRunner { 11 11 12 12 #region Vars 13 [NonSerialized]14 13 private Thread listener; 15 16 [NonSerialized]17 private Thread executor;18 19 [NonSerialized]20 protected static IFormatter formatter = new BinaryFormatter();21 14 #endregion 22 15 23 16 #region Properties 24 public bool QuietMode { get; set; } 17 [Storable] 18 public IEnumerable<AssemblyInfo> AssembliesToLoad { get; set; } 25 19 26 public IEnumerable<AssemblyInfo> AssembliesToLoad{ get; set; }20 internal RunnerHost Host { get; set; } 27 21 #endregion 28 22 23 public void Pause() { 24 var message = new PauseRunnerMessage(); 25 if (Host != null) Host.Send(message); 26 else OnRunnerMessage(message); 27 } 29 28 30 public static void Serialize(IRunner runner, Stream stream) => formatter.Serialize(stream, runner); 31 public static IRunner Deserialize(Stream stream = null) => (IRunner)formatter.Deserialize(stream); 32 public static void Pause(Stream stream) => formatter.Serialize(stream, RunnerJob.Pause); 33 public static void Resume(Stream stream) => formatter.Serialize(stream, RunnerJob.Resume); 34 public static void Cancel(Stream stream) => formatter.Serialize(stream, RunnerJob.Cancel); 29 public void Resume() { 30 var message = new ResumeRunnerMessage(); 31 if (Host != null) Host.Send(message); 32 else OnRunnerMessage(message); 33 } 34 35 public void Cancel() { 36 var message = new CancelRunnerMessage(); 37 if (Host != null) Host.Send(message); 38 else OnRunnerMessage(message); 39 } 35 40 36 41 public void Run() { 37 42 IPluginLoader loader = PluginLoaderFactory.Create(); 38 43 loader.LoadPlugins(AssembliesToLoad); 39 StartExecutor();44 Task t = Task.Run(Execute); 40 45 StartListener(); 41 executor.Join();46 t.Wait(); 42 47 } 43 48 44 49 protected abstract void Execute(); 45 protected abstract void OnRunner Job(RunnerJob runnerJob);50 protected abstract void OnRunnerMessage(RunnerMessage message); 46 51 47 52 #region Helper 48 private void StartExecutor() {49 executor = new Thread(Execute);50 executor.IsBackground = false;51 executor.Start();52 }53 53 54 54 private void StartListener() { 55 55 listener = new Thread(() => { 56 56 Stream stdin = Console.OpenStandardInput(); 57 while (executor.IsAlive) 58 OnRunnerJob((RunnerJob)formatter.Deserialize(stdin)); 57 DateTime lastMessageRecieved = DateTime.MinValue; 58 while (true) { 59 RunnerMessage message = RunnerMessage.ReadMessageFromStream(stdin); 60 if (DateTime.Compare(lastMessageRecieved, message.SendTime) < 0) { 61 OnRunnerMessage(message); 62 lastMessageRecieved = message.SendTime; 63 } 64 } 59 65 }); 60 66 listener.IsBackground = true; -
branches/2924_DotNetCoreMigration/HeuristicLab.PluginInfrastructure/3.3/Isolation/RunnerHost.cs
r16993 r17013 4 4 using System.Threading; 5 5 using System.Threading.Tasks; 6 using HEAL.Attic; 6 7 7 8 namespace HeuristicLab.PluginInfrastructure { … … 14 15 Starting, 15 16 Running, 16 Paused,17 Stopping,18 17 Stopped 19 18 } … … 27 26 #region Vars 28 27 protected Process process; 28 private ProtoBufSerializer serializer = new ProtoBufSerializer(); 29 29 #endregion 30 30 31 31 #region Properties 32 /// <summary> 33 /// Set this to true, if console output should be disabled. 34 /// </summary> 35 public bool QuietMode { get; set; } 36 37 32 38 /// <summary> 33 39 /// The programm, which should be used. For example 'docker'. … … 41 47 protected string Password { get; private set; } 42 48 protected string Domain { get; private set; } 49 protected IRunner Runner { get; set; } 43 50 44 /// <summary>45 /// The runner state.46 /// </summary>47 51 public RunnerState State { get; protected set; } = RunnerState.Created; 48 52 #endregion 49 53 50 54 #region Constructors 55 51 56 protected RunnerHost(string program, string startArgument, string userName, string password, string domain) { 52 57 Program = program; … … 63 68 throw new InvalidOperationException("Runner must be in state 'Created'."); 64 69 State = RunnerState.Starting; 70 Runner = runner; 65 71 process = new Process { 66 72 StartInfo = new ProcessStartInfo { … … 72 78 RedirectStandardError = true, 73 79 CreateNoWindow = false, 74 UserName = string.IsNullOrEmpty(UserName) ? null : UserName, 80 UserName = string.IsNullOrEmpty(UserName) ? null : UserName, // TODO: accounts testen: https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/local-accounts#sec-localsystem 75 81 PasswordInClearText = string.IsNullOrEmpty(Password) ? null : Password, 76 82 Domain = string.IsNullOrEmpty(Domain) ? null : Domain, … … 80 86 }; 81 87 process.Start(); 88 82 89 // registers a task for cancellation, prevents the use of polling (while-loop) 83 90 var task = RegisterCancellation(token.HasValue ? token.Value : CancellationToken.None); 84 91 85 // write config to standardinput, runner listens on this and deserializes the config 86 Runner.Serialize(runner, process.StandardInput.BaseStream); 92 // set runnerhost in runner 93 Runner r = Runner as Runner; 94 if (r != null) r.Host = this; 87 95 88 96 process.BeginOutputReadLine(); 89 97 process.BeginErrorReadLine(); 90 98 91 if (! runner.QuietMode) {99 if (!QuietMode) { 92 100 process.OutputDataReceived += (s, e) => Console.WriteLine(e.Data); 93 101 process.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data); 94 102 } 103 104 // write config to standardinput, runner listens on this and deserializes the config 105 SendMessage(new TransportRunnerMessage(Runner)); 106 95 107 State = RunnerState.Running; 96 108 if (await task) State = RunnerState.Cancelled; 97 109 else State = RunnerState.Stopped; 98 99 110 } 100 111 101 /// <summary> 102 /// Pauses the runners. The childprocess gets notified with the help of the standard input and a serialized RunnerJob. 103 /// To pause the runner, the state must be "Running". 104 /// </summary> 105 public void Pause() { 112 113 public void Send(RunnerMessage runnerMessage) { 106 114 if (State != RunnerState.Running) throw new InvalidOperationException("Runner must be in state 'Running'!"); 107 Runner.Pause(process.StandardInput.BaseStream); 108 State = RunnerState.Paused; 115 SendMessage(runnerMessage); 109 116 } 110 117 111 /// <summary> 112 /// Resumes a paused runner. The childprocess gets notified with the help of the standard input and a serialized RunnerJob. 113 /// To resume the runner, the state must be "Paused". 114 /// </summary> 115 public void Resume() { 116 if (State != RunnerState.Paused) throw new InvalidOperationException("Runner must be in state 'Paused'!"); 117 Runner.Resume(process.StandardInput.BaseStream); 118 State = RunnerState.Running; 118 // because we need to transfer the runner with a TransportRunnerMessage in the starting state and the 119 // original send method should not be available until running state 120 protected virtual void SendMessage(RunnerMessage runnerMessage) { 121 runnerMessage.SendTime = DateTime.Now; 122 byte[] bytes = serializer.Serialize(runnerMessage); 123 byte[] size = BitConverter.GetBytes(bytes.Length); 124 process.StandardInput.BaseStream.Write(size, 0, size.Length); 125 process.StandardInput.BaseStream.Flush(); 126 process.StandardInput.BaseStream.Write(bytes, 0, bytes.Length); 127 process.StandardInput.BaseStream.Flush(); 119 128 } 120 129 … … 125 134 /// When the child process gets finished without requested cancellation, the linked token gets cancelled and a result set. 126 135 /// </summary> 127 pr ivateTask<bool> RegisterCancellation(CancellationToken token) {136 protected Task<bool> RegisterCancellation(CancellationToken token) { 128 137 if (process != null && State == RunnerState.Starting) { 129 138 var cts = CancellationTokenSource.CreateLinkedTokenSource(token); … … 133 142 cts.Token.Register(() => { 134 143 if (!process.HasExited) { 135 Runner.Cancel( process.StandardInput.BaseStream);144 Runner.Cancel(); 136 145 process.WaitForExit(); 137 146 }
Note: See TracChangeset
for help on using the changeset viewer.