public void ProcessCommand(IExecutionContext context, Command command) { // queue async command task to run agent plugin. context.Debug($"Process {command.Area}.{command.Event} command through agent plugin in backend."); if (!_supportedLoggingCommands.ContainsKey(command.Area) || !_supportedLoggingCommands[command.Area].ContainsKey(command.Event)) { throw new NotSupportedException(command.ToString()); } var plugin = _supportedLoggingCommands[command.Area][command.Event]; ArgUtil.NotNull(plugin, nameof(plugin)); ArgUtil.NotNullOrEmpty(plugin.DisplayName, nameof(plugin.DisplayName)); // construct plugin context AgentCommandPluginExecutionContext pluginContext = new AgentCommandPluginExecutionContext { Data = command.Data, Properties = command.Properties, Endpoints = context.Endpoints, }; var target = context.StepTarget(); Variables.TranslationMethod translateToHostPath = Variables.DefaultStringTranslator; ContainerInfo containerInfo = target as ContainerInfo; // Since plugins run on the host, but the inputs and variables have already been translated // to the container path, we need to convert them back to the host path // TODO: look to see if there is a better way to not have translate these back if (containerInfo != null) { translateToHostPath = (string val) => { return(containerInfo.TranslateToHostPath(val)); }; } // variables context.Variables.CopyInto(pluginContext.Variables, translateToHostPath); var commandContext = HostContext.CreateService <IAsyncCommandContext>(); commandContext.InitializeCommandContext(context, plugin.DisplayName); commandContext.Task = ProcessPluginCommandAsync(commandContext, pluginContext, plugin.CommandPluginTypeName, command, context.CancellationToken); context.AsyncCommands.Add(commandContext); }
public AgentTaskPluginExecutionContext GeneratePluginExecutionContext(IExecutionContext context, Dictionary <string, string> inputs, Variables runtimeVariables) { ArgUtil.NotNull(context, nameof(context)); ArgUtil.NotNull(inputs, nameof(inputs)); ArgUtil.NotNull(runtimeVariables, nameof(runtimeVariables)); // construct plugin context var target = context.StepTarget(); Variables.TranslationMethod translateToHostPath = Variables.DefaultStringTranslator; ContainerInfo containerInfo = target as ContainerInfo; // Since plugins run on the host, but the inputs and variables have already been translated // to the container path, we need to convert them back to the host path // TODO: look to see if there is a better way to not have translate these back if (containerInfo != null) { var newInputs = new Dictionary <string, string>(); foreach (var entry in inputs) { newInputs[entry.Key] = containerInfo.TranslateToHostPath(entry.Value); } inputs = newInputs; translateToHostPath = (string val) => { return(containerInfo.TranslateToHostPath(val)); }; } AgentTaskPluginExecutionContext pluginContext = new AgentTaskPluginExecutionContext { Inputs = inputs, Repositories = context.Repositories, Endpoints = context.Endpoints, Container = containerInfo, //TODO: Figure out if this needs to have all the containers or just the one for the current step JobSettings = context.JobSettings, }; // variables runtimeVariables.CopyInto(pluginContext.Variables, translateToHostPath); context.TaskVariables.CopyInto(pluginContext.TaskVariables, translateToHostPath); return(pluginContext); }
public async Task RunPluginTaskAsync(IExecutionContext context, string plugin, Dictionary <string, string> inputs, Dictionary <string, string> environment, Variables runtimeVariables, EventHandler <ProcessDataReceivedEventArgs> outputHandler) { ArgUtil.NotNullOrEmpty(plugin, nameof(plugin)); // Only allow plugins we defined if (!_taskPlugins.Contains(plugin)) { throw new NotSupportedException(plugin); } // Resolve the working directory. string workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); ArgUtil.Directory(workingDirectory, nameof(workingDirectory)); // Agent.PluginHost string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), $"Agent.PluginHost{Util.IOUtil.ExeExtension}"); ArgUtil.File(file, $"Agent.PluginHost{Util.IOUtil.ExeExtension}"); // Agent.PluginHost's arguments string arguments = $"task \"{plugin}\""; // construct plugin context var target = context.StepTarget(); Variables.TranslationMethod translateToHostPath = Variables.DefaultStringTranslator; ContainerInfo containerInfo = target as ContainerInfo; // Since plugins run on the host, but the inputs and variables have already been translated // to the container path, we need to convert them back to the host path // TODO: look to see if there is a better way to not have translate these back if (containerInfo != null) { foreach (var key in inputs.Keys) { inputs[key] = containerInfo.TranslateToHostPath(inputs[key]); } translateToHostPath = (string val) => { return(containerInfo.TranslateToHostPath(val)); }; } AgentTaskPluginExecutionContext pluginContext = new AgentTaskPluginExecutionContext { Inputs = inputs, Repositories = context.Repositories, Endpoints = context.Endpoints, Container = containerInfo, //TODO: Figure out if this needs to have all the containers or just the one for the current step JobSettings = context.JobSettings, }; // variables runtimeVariables.CopyInto(pluginContext.Variables, translateToHostPath); context.TaskVariables.CopyInto(pluginContext.TaskVariables, translateToHostPath); using (var processInvoker = HostContext.CreateService <IProcessInvoker>()) { var redirectStandardIn = new InputQueue <string>(); redirectStandardIn.Enqueue(JsonUtility.ToString(pluginContext)); processInvoker.OutputDataReceived += outputHandler; processInvoker.ErrorDataReceived += outputHandler; // Execute the process. Exit code 0 should always be returned. // A non-zero exit code indicates infrastructural failure. // Task failure should be communicated over STDOUT using ## commands. await processInvoker.ExecuteAsync(workingDirectory : workingDirectory, fileName : file, arguments : arguments, environment : environment, requireExitCodeZero : true, outputEncoding : Encoding.UTF8, killProcessOnCancel : false, redirectStandardIn : redirectStandardIn, cancellationToken : context.CancellationToken); } }
public Task StartAsync(IExecutionContext context, List <IStep> steps, CancellationToken token) { Trace.Entering(); ArgUtil.NotNull(context, nameof(context)); List <PluginInfo> enabledPlugins = new List <PluginInfo>(); if (context.Variables.GetBoolean("agent.disablelogplugin") ?? false) { // all log plugs are disabled context.Debug("All log plugins are disabled."); } else { foreach (var plugin in _logPlugins) { if (context.Variables.GetBoolean($"agent.disablelogplugin.{plugin.Key}") ?? false) { // skip plugin context.Debug($"Log plugin '{plugin.Key}' is disabled."); continue; } else { enabledPlugins.Add(plugin.Value); } } } if (enabledPlugins.Count > 0) { // Resolve the working directory. string workingDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); ArgUtil.Directory(workingDirectory, nameof(workingDirectory)); // Agent.PluginHost string file = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), $"Agent.PluginHost{Util.IOUtil.ExeExtension}"); ArgUtil.File(file, $"Agent.PluginHost{Util.IOUtil.ExeExtension}"); // Agent.PluginHost's arguments string arguments = $"log \"{_instanceId.ToString("D")}\""; var processInvoker = HostContext.CreateService <IProcessInvoker>(); processInvoker.OutputDataReceived += (object sender, ProcessDataReceivedEventArgs e) => { if (e.Data != null) { _outputs.Enqueue(e.Data); } }; processInvoker.ErrorDataReceived += (object sender, ProcessDataReceivedEventArgs e) => { if (e.Data != null) { _outputs.Enqueue(e.Data); } }; _pluginHostProcess?.Dispose(); _pluginHostProcess = processInvoker.ExecuteAsync(workingDirectory: workingDirectory, fileName: file, arguments: arguments, environment: null, requireExitCodeZero: true, outputEncoding: Encoding.UTF8, killProcessOnCancel: true, redirectStandardIn: _redirectedStdin, inheritConsoleHandler: false, keepStandardInOpen: true, cancellationToken: token); // construct plugin context AgentLogPluginHostContext pluginContext = new AgentLogPluginHostContext { PluginAssemblies = new List <string>(), Repositories = context.Repositories, Endpoints = context.Endpoints, Variables = new Dictionary <string, VariableValue>(), Steps = new Dictionary <string, Pipelines.TaskStepDefinitionReference>() }; // plugins pluginContext.PluginAssemblies.AddRange(_logPlugins.Values.Select(x => x.AssemblyName)); var target = context.StepTarget(); Variables.TranslationMethod translateToHostPath = Variables.DefaultStringTranslator; ContainerInfo containerInfo = target as ContainerInfo; // Since plugins run on the host, but the inputs and variables have already been translated // to the container path, we need to convert them back to the host path // TODO: look to see if there is a better way to not have translate these back if (containerInfo != null) { translateToHostPath = (string val) => { return(containerInfo.TranslateToHostPath(val)); }; } // variables context.Variables.CopyInto(pluginContext.Variables, translateToHostPath); // steps foreach (var step in steps) { var taskStep = step as ITaskRunner; if (taskStep != null) { pluginContext.Steps[taskStep.ExecutionContext.Id.ToString("D")] = taskStep.Task.Reference; } } Trace.Info("Send serialized context through STDIN"); _redirectedStdin.Enqueue(JsonUtility.ToString(pluginContext)); foreach (var plugin in _logPlugins) { context.Output($"Plugin: '{plugin.Value.FriendlyName}' is running in background."); } } return(Task.CompletedTask); }