1
- using DynamicPowerShellApi . Logging ;
1
+ using System . IO ;
2
+ using System . Management . Automation ;
3
+ using System . Web . Http . Results ;
4
+ using DynamicPowerShellApi . Jobs ;
5
+ using DynamicPowerShellApi . Logging ;
2
6
using DynamicPowerShellApi . Model ;
7
+ using Newtonsoft . Json ;
3
8
4
9
namespace DynamicPowerShellApi . Controllers
5
10
{
6
11
using Configuration ;
7
12
using Exceptions ;
13
+ using Microsoft . Owin ;
8
14
using Newtonsoft . Json . Linq ;
9
15
using System ;
10
16
using System . Collections . Generic ;
@@ -29,26 +35,35 @@ public class GenericController : ApiController
29
35
/// </summary>
30
36
private readonly ICrashLogger _crashLogger ;
31
37
38
+ /// <summary> The job list provider. </summary>
39
+ private readonly IJobListProvider _jobListProvider ;
40
+
32
41
/// <summary>
33
42
/// Initialises a new instance of the <see cref="GenericController"/> class.
34
43
/// </summary>
35
44
public GenericController ( )
36
45
{
37
46
}
38
47
39
- /// <summary>
40
- /// Initialises a new instance of the <see cref="GenericController"/> class.
41
- /// </summary>
42
- /// <param name="powershellRunner">
43
- /// The PowerShell runner.
44
- /// </param>
45
- /// <param name="crashLogger">
46
- /// An implementation of a crash logger.
47
- /// </param>
48
- public GenericController ( IRunner powershellRunner , ICrashLogger crashLogger )
48
+ /// <summary> Initialises a new instance of the <see cref="GenericController"/> class. </summary>
49
+ /// <remarks> Anthony, 6/1/2015. </remarks>
50
+ /// <exception cref="ArgumentNullException"> Thrown when one or more required arguments are
51
+ /// null. </exception>
52
+ /// <param name="powershellRunner"> The PowerShell runner. </param>
53
+ /// <param name="crashLogger"> An implementation of a crash logger. </param>
54
+ /// <param name="jobListProvider"> The job list provider. </param>
55
+ public GenericController ( IRunner powershellRunner , ICrashLogger crashLogger , IJobListProvider jobListProvider )
49
56
{
57
+ if ( jobListProvider == null )
58
+ throw new ArgumentNullException ( "jobListProvider" ) ;
59
+ if ( crashLogger == null )
60
+ throw new ArgumentNullException ( "crashLogger" ) ;
61
+ if ( powershellRunner == null )
62
+ throw new ArgumentNullException ( "powershellRunner" ) ;
63
+
50
64
_powershellRunner = powershellRunner ;
51
65
_crashLogger = crashLogger ;
66
+ _jobListProvider = jobListProvider ;
52
67
}
53
68
54
69
/// <summary>
@@ -59,7 +74,40 @@ public GenericController(IRunner powershellRunner, ICrashLogger crashLogger)
59
74
[ AllowAnonymous ]
60
75
public HttpResponseMessage Status ( )
61
76
{
62
- return new HttpResponseMessage { Content = new StringContent ( "OK" ) } ;
77
+ return new HttpResponseMessage { Content = new StringContent ( "OK" ) } ;
78
+ }
79
+
80
+ [ Route ( "jobs" ) ]
81
+ public dynamic AllJobStatus ( )
82
+ {
83
+ dynamic jobs = new
84
+ {
85
+ running = _jobListProvider . GetRunningJobs ( ) ,
86
+ completed = _jobListProvider . GetCompletedJobs ( )
87
+ } ;
88
+
89
+ return jobs ;
90
+ }
91
+
92
+ /// <summary> Gets a job. </summary>
93
+ /// <remarks> Anthony, 6/1/2015. </remarks>
94
+ /// <exception cref="ArgumentOutOfRangeException"> Thrown when one or more arguments are outside
95
+ /// the required range. </exception>
96
+ /// <param name="jobId"> Identifier for the job. </param>
97
+ /// <returns> The job. </returns>
98
+ [ Route ( "job" ) ]
99
+ public dynamic GetJob ( string jobId )
100
+ {
101
+ Guid jobGuid ;
102
+ if ( ! Guid . TryParse ( jobId , out jobGuid ) )
103
+ throw new ArgumentOutOfRangeException ( "jobId is not a valid GUID" ) ;
104
+
105
+ using ( TextReader reader = new StreamReader (
106
+ Path . Combine ( WebApiConfiguration . Instance . Jobs . JobStorePath , jobId + ".json" ) ) )
107
+ {
108
+ dynamic d = JObject . Parse ( reader . ReadToEnd ( ) ) ;
109
+ return d ;
110
+ }
63
111
}
64
112
65
113
/// <summary>
@@ -79,7 +127,7 @@ public HttpResponseMessage Status()
79
127
/// <exception cref="Exception">
80
128
/// </exception>
81
129
[ AuthorizeIfEnabled ]
82
- public async Task < HttpResponseMessage > ProcessRequestAsync ( )
130
+ public async Task < HttpResponseMessage > ProcessRequestAsync ( HttpRequestMessage request = null )
83
131
{
84
132
DynamicPowershellApiEvents
85
133
. Raise
@@ -168,13 +216,114 @@ public async Task<HttpResponseMessage> ProcessRequestAsync()
168
216
{
169
217
DynamicPowershellApiEvents . Raise . VerboseMessaging ( String . Format ( "Started Executing the runner" ) ) ;
170
218
171
- PowershellReturn output =
172
- await _powershellRunner . ExecuteAsync ( method . PowerShellPath , method . Snapin , method . Module , query2 . ToList ( ) , asJob ) ;
219
+ if ( ! asJob )
220
+ {
221
+ PowershellReturn output =
222
+ await _powershellRunner . ExecuteAsync ( method . PowerShellPath , method . Snapin , method . Module , query2 . ToList ( ) , asJob ) ;
223
+
224
+ JToken token = output . ActualPowerShellData . StartsWith ( "[" )
225
+ ? ( JToken ) JArray . Parse ( output . ActualPowerShellData )
226
+ : JObject . Parse ( output . ActualPowerShellData ) ;
173
227
174
- JToken token = output . ActualPowerShellData . StartsWith ( "[" )
175
- ? ( JToken ) JArray . Parse ( output . ActualPowerShellData )
176
- : JObject . Parse ( output . ActualPowerShellData ) ;
177
- return new HttpResponseMessage { Content = new JsonContent ( token ) } ;
228
+ return new HttpResponseMessage
229
+ {
230
+ Content = new JsonContent ( token )
231
+ } ;
232
+ }
233
+ else // run as job.
234
+ {
235
+ Guid jobId = Guid . NewGuid ( ) ;
236
+ string requestedHost = String . Empty ;
237
+
238
+ if ( Request . Properties . ContainsKey ( "MS_OwinContext" ) )
239
+ requestedHost = ( ( OwinContext ) Request . Properties [ "MS_OwinContext" ] ) . Request . RemoteIpAddress ;
240
+
241
+ _jobListProvider . AddRequestedJob ( jobId , requestedHost ) ;
242
+
243
+ // Go off and run this job please sir.
244
+ var task = Task < bool > . Factory . StartNew (
245
+ ( ) =>
246
+ {
247
+ try
248
+ {
249
+ Task < PowershellReturn > goTask =
250
+ _powershellRunner . ExecuteAsync (
251
+ method . PowerShellPath ,
252
+ method . Snapin ,
253
+ method . Module ,
254
+ query2 . ToList ( ) ,
255
+ true ) ;
256
+
257
+ goTask . Wait ( ) ;
258
+ var output = goTask . Result ;
259
+
260
+ JToken token = output . ActualPowerShellData . StartsWith ( "[" )
261
+ ? ( JToken ) JArray . Parse ( output . ActualPowerShellData )
262
+ : JObject . Parse ( output . ActualPowerShellData ) ;
263
+
264
+ _jobListProvider . CompleteJob ( jobId , output . PowerShellReturnedValidData , String . Empty ) ;
265
+
266
+ string outputPath = Path . Combine ( WebApiConfiguration . Instance . Jobs . JobStorePath , jobId + ".json" ) ;
267
+
268
+ using ( TextWriter writer = File . CreateText ( outputPath ) )
269
+ {
270
+ JsonSerializer serializer = new JsonSerializer
271
+ {
272
+ Formatting = Formatting . Indented // Make it readable for Ops sake!
273
+ } ;
274
+ serializer . Serialize ( writer , token ) ;
275
+ }
276
+
277
+ return true ;
278
+ }
279
+ catch ( PowerShellExecutionException poException )
280
+ {
281
+ CrashLogEntry entry = new CrashLogEntry
282
+ {
283
+ Exceptions = poException . Exceptions ,
284
+ LogTime = poException . LogTime ,
285
+ RequestAddress = requestedHost ,
286
+ RequestMethod = methodName ,
287
+ RequestUrl = Request . RequestUri . ToString ( )
288
+ } ;
289
+ entry . SetActivityId ( activityId ) ;
290
+ string logFile = _crashLogger . SaveLog ( entry ) ;
291
+
292
+ DynamicPowershellApiEvents . Raise . InvalidPowerShellOutput ( poException . Message + " logged to " + logFile ) ;
293
+
294
+ ErrorResponse response =
295
+ new ErrorResponse
296
+ {
297
+ ActivityId = activityId ,
298
+ LogFile = logFile ,
299
+ Message = poException . Message
300
+ } ;
301
+
302
+ JToken token = new JObject ( response ) ;
303
+
304
+ _jobListProvider . CompleteJob ( jobId , false , String . Empty ) ;
305
+
306
+ string outputPath = Path . Combine ( WebApiConfiguration . Instance . Jobs . JobStorePath , jobId + ".json" ) ;
307
+
308
+ using ( TextWriter writer = File . CreateText ( outputPath ) )
309
+ {
310
+ JsonSerializer serializer = new JsonSerializer
311
+ {
312
+ Formatting = Formatting . Indented // Make it readable for Ops sake!
313
+ } ;
314
+ serializer . Serialize ( writer , token ) ;
315
+ }
316
+ return true ;
317
+ }
318
+ }
319
+ ) ;
320
+
321
+ // return the Job ID.
322
+ return new HttpResponseMessage
323
+ {
324
+ Content = new JsonContent ( new JValue ( jobId ) )
325
+ } ;
326
+ }
178
327
}
179
328
catch ( PowerShellExecutionException poException )
180
329
{
@@ -191,7 +340,7 @@ public async Task<HttpResponseMessage> ProcessRequestAsync()
191
340
192
341
DynamicPowershellApiEvents . Raise . InvalidPowerShellOutput ( poException . Message + " logged to " + logFile ) ;
193
342
194
- var response = Request . CreateResponse < ErrorResponse > ( HttpStatusCode . InternalServerError ,
343
+ HttpResponseMessage response = Request . CreateResponse ( HttpStatusCode . InternalServerError ,
195
344
new ErrorResponse
196
345
{
197
346
ActivityId = activityId ,
@@ -206,7 +355,7 @@ public async Task<HttpResponseMessage> ProcessRequestAsync()
206
355
{
207
356
CrashLogEntry entry = new CrashLogEntry
208
357
{
209
- Exceptions = new List < PowerShellException > ( )
358
+ Exceptions = new List < PowerShellException >
210
359
{
211
360
new PowerShellException
212
361
{
@@ -226,7 +375,7 @@ public async Task<HttpResponseMessage> ProcessRequestAsync()
226
375
227
376
DynamicPowershellApiEvents . Raise . UnhandledException ( ex . Message + " logged to " + logFile , ex . StackTrace ?? String . Empty ) ;
228
377
229
- var response = Request . CreateResponse < ErrorResponse > ( HttpStatusCode . InternalServerError ,
378
+ HttpResponseMessage response = Request . CreateResponse ( HttpStatusCode . InternalServerError ,
230
379
new ErrorResponse
231
380
{
232
381
ActivityId = activityId ,
0 commit comments