예제 #1
0
        protected override async Task ExecuteAsync(CancellationToken appStoppingToken)
        {
            // Exactly the same as TestJobMonitoring except the job is JobFac-aware and needs a payload

            try
            {
                Console.WriteLine("Getting job factory proxy.");
                var factory = jobFacServices.GetJobFactory();

                var options = new FactoryStartOptions
                {
                    DefinitionId = "Sample.JobFac.aware"
                                   // we could add the payload here but it's easier to use the StartJob overload
                                   // since FactoryStartOptions stores alternate argument lists and startup
                                   // payloads in a dictionary (which is useful for multi-job sequences)
                };

                string payload = "35,Hello world!";

                Console.WriteLine($"Starting sample job Sample.JobFac.aware with payload: {payload}");
                var jobKey = await factory.StartJob(options, startupPayload : payload);

                // Everything below is identical to TestJobMonitoring
                Console.WriteLine($"Job instance key: {jobKey}");

                var  timeout            = DateTimeOffset.UtcNow.AddSeconds(90);
                bool done               = false;
                IJobExternalProcess job = null;
                while (!done && DateTimeOffset.UtcNow < timeout)
                {
                    appStoppingToken.ThrowIfCancellationRequested();
                    Console.WriteLine("Pausing 10 seconds then reading status.");
                    await Task.Delay(10000);

                    if (job == null)
                    {
                        job = jobFacServices.GetExternalProcessJob(jobKey);
                    }
                    if (job == null)
                    {
                        Console.WriteLine("Failed to obtain job proxy.");
                        done = true;
                        break;
                    }

                    var status = await job.GetStatus();

                    Console.WriteLine($"Status {status.RunStatus} last updated {status.LastUpdated.ToLocalTime()}");
                    done = status.HasExited;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"\n\nException:\n{ex}");
            }
            finally
            {
                appLifetime.StopApplication();
            }
        }
예제 #2
0
        // TODO respond to appStoppingToken cancellation
        protected override async Task ExecuteAsync(CancellationToken appStoppingToken)
        {
            IJobExternalProcess jobService = null;

            try
            {
                logger.LogTrace($"Runner starting, job instance {Program.JobInstanceKey}");
                jobService = jobFacServices.GetExternalProcessJob(Program.JobInstanceKey);
                if (jobService == null)
                {
                    logger.LogError("Runner was unable to obtain a reference to the Job service");
                    return;
                }

                await jobService.UpdateRunStatus(RunStatus.StartRequested);

                var jobDef = await jobService.GetDefinition();
                await RunJob(jobService, jobDef);
            }
            catch (Exception ex)
            {
                logger.LogError($"ExecuteAsync {ex}");
                await jobService.UpdateExitMessage(RunStatus.Unknown, -1, $"JobFac.Services.Runner exception {ex}");
            }
            finally
            {
                logger.LogInformation($"Runner ending, calling StopApplication after log-flush delay");
                await Task.Delay(10000);

                appLifetime.StopApplication();
            }
        }
예제 #3
0
        protected override async Task InitializingAsync(CancellationToken cancelInitToken)
        {
            IJobExternalProcess service = null;

            try
            {
                var args = options.CommandLineArgs.Length > 1 ? options.CommandLineArgs[1..^ 1] : new string[0];
예제 #4
0
        protected override async Task ExecuteAsync(CancellationToken appStoppingToken)
        {
            // Exactly the same as TestJobPayload except the job isn't JobFac-aware

            try
            {
                Console.WriteLine("Getting job factory proxy.");
                var factory = jobFacServices.GetJobFactory();

                var options = new FactoryStartOptions
                {
                    DefinitionId = "Sample.JobFac.unaware"
                };

                Console.WriteLine("Starting sample job: Sample.JobFac.unaware");
                var jobKey = await factory.StartJob(options);

                Console.WriteLine($"Job instance key: {jobKey}");

                var  timeout            = DateTimeOffset.UtcNow.AddSeconds(90);
                bool done               = false;
                IJobExternalProcess job = null;
                while (!done && DateTimeOffset.UtcNow < timeout)
                {
                    appStoppingToken.ThrowIfCancellationRequested();
                    Console.WriteLine("Pausing 10 seconds then reading status.");
                    await Task.Delay(10000);

                    if (job == null)
                    {
                        job = jobFacServices.GetExternalProcessJob(jobKey);
                    }
                    if (job == null)
                    {
                        Console.WriteLine("Failed to obtain job proxy.");
                        done = true;
                        break;
                    }

                    var status = await job.GetStatus();

                    Console.WriteLine($"Status {status.RunStatus} last updated {status.LastUpdated.ToLocalTime()}");
                    done = status.HasExited;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"\n\nException:\n{ex}");
            }
            finally
            {
                appLifetime.StopApplication();
            }
        }
예제 #5
0
 public JobFacAwareProcessContext(
     IHostApplicationLifetime hostApplicationLifetime,
     string instanceId, string[] args, string payload,
     IJobExternalProcess jobService,
     IJobFacServiceProvider provider)
 {
     appLifetime        = hostApplicationLifetime;
     JobInstanceId      = instanceId;
     CommandLineArgs    = args;
     StartupPayload     = payload;
     JobService         = jobService;
     JobServiceProvider = provider;
 }
예제 #6
0
        private async Task RunJob(IJobExternalProcess jobService, JobDefinition <DefinitionExternalProcess> jobDef)
        {
            logger.LogTrace($"RunJob starting");
            var tokenSource = new CancellationTokenSource();
            var token       = tokenSource.Token;

            StringBuilder dbStdOut = new StringBuilder();
            StringBuilder dbStdErr = new StringBuilder();

            StreamWriter fileStdOut = null;
            StreamWriter fileStdErr = null;

            var jobProps = jobDef.JobTypeProperties;

            var proc = new Process();

            proc.StartInfo.FileName         = jobProps.ExecutablePathname;
            proc.StartInfo.WorkingDirectory = jobProps.WorkingDirectory;
            proc.StartInfo.UseShellExecute  = false;
            proc.StartInfo.CreateNoWindow   = true;

            // for JobProc-aware jobs, first argument is the job instance key
            proc.StartInfo.Arguments = (jobProps.IsJobFacAware) ? $"{Program.JobInstanceKey} {jobProps.Arguments}" : jobProps.Arguments;

            try
            {
                if (jobProps.CaptureStdOut != JobStreamHandling.None)
                {
                    proc.StartInfo.RedirectStandardOutput = true;

                    if (jobProps.CaptureStdOut == JobStreamHandling.Database)
                    {
                        proc.OutputDataReceived += (s, e) => { if (e?.Data != null)
                                                               {
                                                                   dbStdOut.AppendLine(e.Data);
                                                               }
                        }
                    }
                    ;

                    if (jobProps.CaptureStdOut.IsFileBased())
                    {
                        var pathname = jobProps.CaptureStdOut == JobStreamHandling.TimestampedFile
                            ? jobProps.StdOutPathname.Replace("*", Formatting.FilenameTimestampUtcNow)
                            : jobProps.StdOutPathname;
                        fileStdOut = new StreamWriter(pathname, jobProps.CaptureStdOut == JobStreamHandling.AppendFile);
                        proc.OutputDataReceived += (s, e) => { if (e?.Data != null)
                                                               {
                                                                   fileStdOut.WriteLineAsync(e.Data);
                                                               }
                        };
                    }
                }

                if (jobProps.CaptureStdErr != JobStreamHandling.None)
                {
                    proc.StartInfo.RedirectStandardError = true;

                    if (jobProps.CaptureStdErr == JobStreamHandling.Database)
                    {
                        proc.ErrorDataReceived += (s, e) => { if (e?.Data != null)
                                                              {
                                                                  dbStdErr.AppendLine(e.Data);
                                                              }
                        }
                    }
                    ;

                    if (jobProps.CaptureStdErr.IsFileBased())
                    {
                        var pathname = jobProps.CaptureStdErr == JobStreamHandling.TimestampedFile
                            ? jobProps.StdErrPathname.Replace("*", Formatting.FilenameTimestampUtcNow)
                            : jobProps.StdErrPathname;
                        fileStdErr              = new StreamWriter(pathname, jobProps.CaptureStdErr == JobStreamHandling.AppendFile);
                        proc.ErrorDataReceived += (s, e) => { if (e?.Data != null)
                                                              {
                                                                  fileStdErr.WriteLineAsync(e.Data);
                                                              }
                        };
                    }
                }

                logger.LogTrace($"RunJob calling Process.Start");
                try
                {
                    // Although Start returns a boolean, it isn't useful to us. The documentation
                    // says it returns false if a process is reused, which could happen if Process
                    // was used with the Windows shell to launch a document handler such as Word.
                    // That isn't a supported use-case. However, it can throw exceptions when, for
                    // example, the provided ExecutablePathname is invalid.
                    proc.Start();
                }
                catch (Exception ex)
                {
                    await jobService.UpdateExitMessage(RunStatus.StartFailed, -1, $"Job failed to start, exception {ex}");

                    return;
                }

                // These must be as close to proc.Start as possible to minimize the possibility of lost output
                if (jobProps.CaptureStdOut != JobStreamHandling.None)
                {
                    proc.BeginOutputReadLine();
                }
                if (jobProps.CaptureStdErr != JobStreamHandling.None)
                {
                    proc.BeginErrorReadLine();
                }

                await jobService.UpdateRunStatus(RunStatus.Running);

                // TODO implement maximum run-time token cancellation

                logger.LogTrace($"RunJob awaiting process exit or kill command");
                await Task.WhenAny
                (
                    WaitForProcessExitAsync(proc, token),
                    MonitorKillCommandNamedPipe(token)
                ).ConfigureAwait(false);

                logger.LogTrace($"RunJob await exited, Process.HasExited? {proc.HasExited}");


                if (!proc.HasExited)
                {
                    proc.Kill(true); // still fires process-exit event (WaitForExitAsync still running)
                    await jobService.UpdateExitMessage(RunStatus.Stopped, -1, "Stop command received, process killed");
                }
                else
                {
                    // when true, job is JobProc-aware and should have called UpdateExitMessage
                    if (!jobProps.IsJobFacAware)
                    {
                        var finalStatus = (proc.ExitCode < 0) ? RunStatus.Failed : RunStatus.Ended;
                        await jobService.UpdateExitMessage(finalStatus, proc.ExitCode, string.Empty);
                    }
                    // TODO play it safe and retrieve status and verify JobProc-aware job actually set an exit message?
                }

                logger.LogTrace($"RunJob cancelling token");
                tokenSource.Cancel();

                // The Process WaitForExit call without a timeout value is the
                // only way to asynchronously wait for the streams to drain.
                if (jobProps.CaptureStdOut != JobStreamHandling.None || jobProps.CaptureStdErr != JobStreamHandling.None)
                {
                    proc.WaitForExit();
                }
            }
            catch (Exception ex)
            {
                logger.LogError($"RunJob caught exception {ex}");
            }
            finally
            {
                logger.LogTrace($"RunJob finalizing");

                tokenSource?.Cancel();
                proc?.Close();

                tokenSource?.Dispose();
                proc?.Dispose();

                fileStdOut?.Close();
                fileStdErr?.Close();

                fileStdOut?.Dispose();
                fileStdErr?.Dispose();
            }

            if (jobProps.CaptureStdOut == JobStreamHandling.Database || jobProps.CaptureStdErr == JobStreamHandling.Database)
            {
                await jobService.WriteCapturedOutput(Program.JobInstanceKey, dbStdOut.ToString(), dbStdErr.ToString());
            }

            logger.LogTrace($"RunJob exiting");
        }