forked from Azure/autorest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathServiceController.cs
235 lines (212 loc) · 7.87 KB
/
ServiceController.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using Microsoft.Extensions.Logging;
namespace AutoRest.CSharp.Tests.Utilities
{
/// <summary>
/// Control creation and execution of node.js deserialization service
/// </summary>
public class ServiceController : IDisposable
{
private const string NpmCommand = "npm.cmd";
private const string NpmArgument = "install";
private const string NodeCommand = "node.exe";
private const string NodeArgument = "./startup/www";
private ProcessOutputListener _listener;
private object _sync = new object();
public ServiceController()
{
Port = GetRandomPortNumber();
EnsureService();
}
#if !LEGACY
private static readonly ILogger _logger;
static ServiceController()
{
var factory = new LoggerFactory();
_logger = factory.CreateLogger<ServiceController>();
factory.AddConsole();
}
#endif
/// <summary>
/// Directory containing the acceptance test files.
/// </summary>
private static string AcceptanceTestsPath
{
get
{
var serverPath = Environment.GetEnvironmentVariable("AUTOREST_TEST_SERVER_PATH").CombinePath("server");
if (!serverPath.DirectoryExists())
{
// otherwise walk up the path till we find a folder
serverPath = Path.Combine("dev", "TestServer", "server").FindFolderByWalkingUpPath();
if (serverPath == null)
{
throw new Exception("Unable to find TestServerPath.\r\n");
}
}
return serverPath;
}
}
/// <summary>
/// Teardown action
/// </summary>
public Action TearDown { get; set; }
/// <summary>
/// Port number the service is listening on.
/// </summary>
public int Port { get; set; }
public Uri Uri
{
get { return new Uri(string.Format(CultureInfo.InvariantCulture, "http://localhost:{0}", Port)); }
}
/// <summary>
/// The process running the service.
/// </summary>
private Process ServiceProcess { get; set; }
public void Dispose()
{
try
{
if (TearDown != null)
{
TearDown();
}
}
catch
{
throw;
}
finally
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
protected virtual void Dispose(bool disposing)
{
if (disposing && ServiceProcess != null && !ServiceProcess.HasExited)
{
EndServiceProcess(ServiceProcess);
ServiceProcess = null;
}
}
/// <summary>
/// Ensure that the node service is running - either create it, or track it if it is already running.
/// </summary>
public void EnsureService()
{
lock (_sync)
{
if (ServiceProcess == null)
{
StartServiceProcess();
}
}
}
public static string GetPathToExecutable(string executableName)
{
var paths = Environment.GetEnvironmentVariable("PATH");
foreach (var path in paths.Split(new[] {Path.PathSeparator}, StringSplitOptions.RemoveEmptyEntries))
{
var fullPath = Path.Combine(path, Path.GetFileName(executableName));
if (File.Exists(fullPath))
{
return fullPath;
}
var ext = Path.GetExtension(executableName);
var exec = (ext == ".cmd" || ext == ".exe") ? Path.GetFileNameWithoutExtension(executableName) : executableName;
fullPath = Path.Combine(path, exec);
if (File.Exists(fullPath))
{
return fullPath;
}
}
return null;
}
private static int GetRandomPortNumber()
{
var rand = new Random();
return rand.Next(3000, 3999);
}
public void StartServiceProcess()
{
var npmPath = GetPathToExecutable(NpmCommand);
if (npmPath == null)
{
throw new InvalidOperationException("Could not find path to " + NpmCommand);
}
using ( var prepareProcess = StartServiceProcess(npmPath, NpmArgument, AcceptanceTestsPath,
waitForServerStart:false))
{
// Wait for maximum of two minutes; One-time preparation.
if (prepareProcess.WaitForExit(120000))
{
var nodePath = GetPathToExecutable(NodeCommand);
if (nodePath == null)
{
throw new InvalidOperationException("Could not find path to " + NodeCommand);
}
ServiceProcess = StartServiceProcess(nodePath, NodeArgument, AcceptanceTestsPath);
}
else
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"Failed to start {0} {1} '{2}'.",
npmPath, NpmArgument, AcceptanceTestsPath));
}
}
}
/// <summary>
/// Run the given command with arguments. Return the result in standard output.
/// </summary>
/// <param name="path">The path to the command to execute.</param>
/// <param name="arguments">The arguments to pass to the command.</param>
/// <param name="workingDirectory">The working directory for the process being launched.</param>
/// <param name="waitForServerStart">Wait for the service to print a start message</param>
/// <returns>The process</returns>
private Process StartServiceProcess(
string path,
string arguments,
string workingDirectory,
bool waitForServerStart = true)
{
_listener = new ProcessOutputListener();
var process = new Process();
var startInfo = process.StartInfo;
startInfo.CreateNoWindow = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.WorkingDirectory = workingDirectory;
startInfo.UseShellExecute = false;
startInfo.FileName = path;
startInfo.Arguments = arguments;
#if !LEGACY
startInfo.Environment["PORT"] = Port.ToString(CultureInfo.InvariantCulture);
#else
startInfo.EnvironmentVariables["PORT"] = Port.ToString(CultureInfo.InvariantCulture);
#endif
process.OutputDataReceived += _listener.ProcessOutput;
process.ErrorDataReceived += _listener.ProcessError;
process.Start();
process.BeginOutputReadLine();
if (waitForServerStart)
{
_listener.ProcessStarted.WaitOne(TimeSpan.FromSeconds(30));
}
return process;
}
private static void EndServiceProcess(Process process)
{
//_logger.LogInformation("Begin killing process...");
process.Kill();
process.WaitForExit(2000);
process.Dispose();
//_logger.LogInformation("Process killed...");
}
}
}