public async Task <int> ExecuteCommand(CommandSettings command) { try { WebProxy.ApplyProxySettings(); _inConfigStage = true; _completedCommand.Reset(); _term.CancelKeyPress += CtrlCHandler; //register a SIGTERM handler HostContext.Unloading += Agent_Unloading; // TODO Unit test to cover this logic Trace.Info(nameof(ExecuteCommand)); var configManager = HostContext.GetService <IConfigurationManager>(); // command is not required, if no command it just starts and/or configures if not configured // TODO: Invalid config prints usage if (command.Help) { PrintUsage(); return(Constants.Agent.ReturnCode.Success); } if (command.Version) { _term.WriteLine(Constants.Agent.Version); return(Constants.Agent.ReturnCode.Success); } if (command.Commit) { _term.WriteLine(BuildConstants.Source.CommitHash); return(Constants.Agent.ReturnCode.Success); } // Unconfigure, remove config files, service and exit if (command.Unconfigure) { try { await configManager.UnconfigureAsync(command); return(Constants.Agent.ReturnCode.Success); } catch (Exception ex) { Trace.Error(ex); _term.WriteError(ex.Message); return(Constants.Agent.ReturnCode.TerminatedError); } } if (command.Run && !configManager.IsConfigured()) { _term.WriteError(StringUtil.Loc("AgentIsNotConfigured")); PrintUsage(); return(Constants.Agent.ReturnCode.TerminatedError); } // unattend mode will not prompt for args if not supplied. Instead will error. if (command.Configure) { try { await configManager.ConfigureAsync(command); return(Constants.Agent.ReturnCode.Success); } catch (Exception ex) { Trace.Error(ex); _term.WriteError(ex.Message); return(Constants.Agent.ReturnCode.TerminatedError); } } Trace.Info("Done evaluating commands"); await configManager.EnsureConfiguredAsync(command); _inConfigStage = false; if (command.NoStart) { Trace.Info("No start."); return(Constants.Agent.ReturnCode.Success); } AgentSettings settings = configManager.LoadSettings(); bool runAsService = configManager.IsServiceConfigured(); if (command.Run || !runAsService) { // Run the agent interactively Trace.Verbose($"Run as service: '{runAsService}'"); return(await RunAsync(TokenSource.Token, settings, runAsService)); } if (runAsService) { // This is helpful if the user tries to start the agent.listener which is already configured or running as service // However user can execute the agent by calling the run command // TODO: Should we check if the service is running and prompt user to start the service if its not already running? _term.WriteLine(StringUtil.Loc("ConfiguredAsRunAsService")); } return(Constants.Agent.ReturnCode.Success); } finally { _term.CancelKeyPress -= CtrlCHandler; HostContext.Unloading -= Agent_Unloading; _completedCommand.Set(); } }
public async Task <int> RunAsync(string pipeIn, string pipeOut) { // Validate args. ArgUtil.NotNullOrEmpty(pipeIn, nameof(pipeIn)); ArgUtil.NotNullOrEmpty(pipeOut, nameof(pipeOut)); WebProxy.ApplyProxySettings(); var jobRunner = HostContext.GetService <IJobRunner>(); using (var channel = HostContext.CreateService <IProcessChannel>()) using (var jobRequestCancellationToken = new CancellationTokenSource()) using (var channelTokenSource = new CancellationTokenSource()) { // Start the channel. channel.StartClient(pipeIn, pipeOut); // Wait for up to 30 seconds for a message from the channel. Trace.Info("Waiting to receive the job message from the channel."); WorkerMessage channelMessage; using (var csChannelMessage = new CancellationTokenSource(_workerStartTimeout)) { channelMessage = await channel.ReceiveAsync(csChannelMessage.Token); } // Deserialize the job message. Trace.Info("Message received."); ArgUtil.Equal(MessageType.NewJobRequest, channelMessage.MessageType, nameof(channelMessage.MessageType)); ArgUtil.NotNullOrEmpty(channelMessage.Body, nameof(channelMessage.Body)); var jobMessage = JsonUtility.FromString <JobRequestMessage>(channelMessage.Body); ArgUtil.NotNull(jobMessage, nameof(jobMessage)); // Initialize the secret masker and set the thread culture. InitializeSecretMasker(jobMessage); SetCulture(jobMessage); // Start the job. Trace.Info($"Job message: {channelMessage.Body}"); Task <TaskResult> jobRunnerTask = jobRunner.RunAsync(jobMessage, jobRequestCancellationToken.Token); // Start listening for a cancel message from the channel. Trace.Info("Listening for cancel message from the channel."); Task <WorkerMessage> channelTask = channel.ReceiveAsync(channelTokenSource.Token); // Wait for one of the tasks to complete. Trace.Info("Waiting for the job to complete or for a cancel message from the channel."); Task.WaitAny(jobRunnerTask, channelTask); // Handle if the job completed. if (jobRunnerTask.IsCompleted) { Trace.Info("Job completed."); channelTokenSource.Cancel(); // Cancel waiting for a message from the channel. return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask)); } // Otherwise a cancel message was received from the channel. Trace.Info("Cancellation message received."); channelMessage = await channelTask; ArgUtil.Equal(MessageType.CancelRequest, channelMessage.MessageType, nameof(channelMessage.MessageType)); jobRequestCancellationToken.Cancel(); // Expire the host cancellation token. // Await the job. return(TaskResultUtil.TranslateToReturnCode(await jobRunnerTask)); } }