public async Task LogPluginHost_HandleProcessExceptions() { using (TestHostContext tc = new TestHostContext(this)) { AgentLogPluginHostContext hostContext = CreateTestLogPluginHostContext(); hostContext.Variables["throw_process"] = "1"; List <IAgentLogPlugin> plugins = new List <IAgentLogPlugin>() { new TestPlugin1(), new TestPluginException() }; TestTrace trace = new TestTrace(tc); AgentLogPluginHost logPluginHost = new AgentLogPluginHost(hostContext, plugins, trace); var task = logPluginHost.Run(); for (int i = 0; i < 1000; i++) { logPluginHost.EnqueueOutput($"{Guid.Empty.ToString("D")}:{i}"); } await Task.Delay(1000); logPluginHost.Finish(); await task; // regular one still running Assert.True(trace.Outputs.Contains("Test1: 0")); Assert.True(trace.Outputs.Contains("Test1: 999")); Assert.True(trace.Outputs.Contains("Test1: Done")); Assert.True(!trace.Outputs.Contains("TestException: 0")); Assert.True(!trace.Outputs.Contains("TestException: 999")); Assert.True(trace.Outputs.Contains("TestException: Done")); } }
public async Task LogPluginHost_NotInitialized() { using (TestHostContext tc = new TestHostContext(this)) { AgentLogPluginHostContext hostContext = CreateTestLogPluginHostContext(); List <IAgentLogPlugin> plugins = new List <IAgentLogPlugin>() { new TestPlugin1(), new TestPluginNotInitialized() }; TestTrace trace = new TestTrace(tc); AgentLogPluginHost logPluginHost = new AgentLogPluginHost(hostContext, plugins, trace); var task = logPluginHost.Run(); for (int i = 0; i < 1000; i++) { logPluginHost.EnqueueOutput($"{Guid.Empty.ToString("D")}:{i}"); } await Task.Delay(1000); logPluginHost.Finish(); await task; // regular one still running Assert.True(trace.Outputs.Contains("Test1: 0")); Assert.True(trace.Outputs.Contains("Test1: 999")); Assert.True(trace.Outputs.Contains("Test1: Done")); Assert.True(!trace.Outputs.Contains("TestNotInitialized: 0")); Assert.True(!trace.Outputs.Contains("TestNotInitialized: Done")); } }
public async Task LogPluginHost_SlowPluginRecover() { using (TestHostContext tc = new TestHostContext(this)) { AgentLogPluginHostContext hostContext = CreateTestLogPluginHostContext(); List <IAgentLogPlugin> plugins = new List <IAgentLogPlugin>() { new TestPlugin1(), new TestPluginSlowRecover() }; TestTrace trace = new TestTrace(tc); AgentLogPluginHost logPluginHost = new AgentLogPluginHost(hostContext, plugins, trace, 950, 100); var task = logPluginHost.Run(); for (int i = 0; i < 1000; i++) { logPluginHost.EnqueueOutput($"{Guid.Empty.ToString("D")}:{i}"); } await Task.Delay(1000); logPluginHost.Finish(); await task; // regular one still running Assert.True(trace.Outputs.Contains("Test1: 0")); Assert.True(trace.Outputs.Contains("Test1: 999")); Assert.True(trace.Outputs.Contains("Test1: Done")); Assert.True(trace.Outputs.Contains("TestSlowRecover: Done")); Assert.True(trace.Outputs.Exists(x => x.Contains("TestPluginSlowRecover' has too many buffered outputs."))); Assert.True(trace.Outputs.Exists(x => x.Contains("TestPluginSlowRecover' has cleared out buffered outputs."))); } }
public async Task LogPluginHost_ShortCircuitSlowPlugin() { using (TestHostContext tc = new TestHostContext(this)) { AgentLogPluginHostContext hostContext = CreateTestLogPluginHostContext(); List <IAgentLogPlugin> plugins = new List <IAgentLogPlugin>() { new TestPlugin1(), new TestPluginSlow() }; TestTrace trace = new TestTrace(tc); AgentLogPluginHost logPluginHost = new AgentLogPluginHost(hostContext, plugins, trace, 100, 100); var task = logPluginHost.Run(); for (int i = 0; i < 1000; i++) { logPluginHost.EnqueueOutput($"{Guid.Empty.ToString("D")}:{i}"); } await Task.Delay(1000); logPluginHost.Finish(); await task; // regular one still running Assert.True(trace.Outputs.Contains("Test1: 0")); Assert.True(trace.Outputs.Contains("Test1: 999")); Assert.True(trace.Outputs.Contains("Test1: Done")); // slow one got killed Assert.False(trace.Outputs.Contains("TestSlow: Done")); Assert.True(trace.Outputs.Exists(x => x.Contains("Plugin has been short circuited"))); } }
public async Task LogPluginHost_RunMultiplePlugins() { using (TestHostContext tc = new TestHostContext(this)) { AgentLogPluginHostContext hostContext = CreateTestLogPluginHostContext(); List <IAgentLogPlugin> plugins = new List <IAgentLogPlugin>() { new TestPlugin1(), new TestPlugin2() }; TestTrace trace = new TestTrace(tc); AgentLogPluginHost logPluginHost = new AgentLogPluginHost(hostContext, plugins, trace); var task = logPluginHost.Run(); for (int i = 0; i < 1000; i++) { logPluginHost.EnqueueOutput($"{Guid.Empty.ToString("D")}:{i}"); } await Task.Delay(1000); logPluginHost.Finish(); await task; foreach (var fragment in new string[] { "Test1: 0", "Test1: 999", "Test1: Done", "Test2: 0", "Test2: 999", "Test2: Done" }) { Assert.True(trace.Outputs.Contains(fragment), $"Found '{fragment}' in: {trace.Outputs}"); } } }
public async Task LogPluginHost_RunSinglePluginWithEmptyLinesInput() { using (TestHostContext tc = new TestHostContext(this)) { AgentLogPluginHostContext hostContext = CreateTestLogPluginHostContext(); List <IAgentLogPlugin> plugins = new List <IAgentLogPlugin>() { new TestPlugin1() }; TestTrace trace = new TestTrace(tc); AgentLogPluginHost logPluginHost = new AgentLogPluginHost(hostContext, plugins, trace); var task = logPluginHost.Run(); for (int i = 0; i < 100; i++) { logPluginHost.EnqueueOutput($"{Guid.Empty.ToString("D")}:{i}"); } for (int i = 0; i < 100; i++) { logPluginHost.EnqueueOutput($"{Guid.Empty.ToString("D")}:{i}"); } for (int i = 0; i < 10; i++) { logPluginHost.EnqueueOutput($"{Guid.Empty.ToString("D")}:"); } for (int i = 100; i < 200; i++) { logPluginHost.EnqueueOutput($"{Guid.Empty.ToString("D")}:{i}"); } await Task.Delay(1000); logPluginHost.Finish(); await task; Assert.True(trace.Outputs.Contains("Test1: 0")); Assert.True(trace.Outputs.Contains("Test1: 99")); Assert.True(trace.Outputs.Contains("Test1: ")); Assert.True(trace.Outputs.Contains("Test1: 100")); Assert.True(trace.Outputs.Contains("Test1: 199")); Assert.Equal(10, trace.Outputs.FindAll(x => x == "Test1: ").Count); } }
public static int Main(string[] args) { if (PlatformUtil.UseLegacyHttpHandler) { AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false); } Console.CancelKeyPress += Console_CancelKeyPress; // Set encoding to UTF8, process invoker will use UTF8 write to STDIN Console.InputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8; try { ArgUtil.NotNull(args, nameof(args)); ArgUtil.Equal(2, args.Length, nameof(args.Length)); string pluginType = args[0]; if (string.Equals("task", pluginType, StringComparison.OrdinalIgnoreCase)) { string assemblyQualifiedName = args[1]; ArgUtil.NotNullOrEmpty(assemblyQualifiedName, nameof(assemblyQualifiedName)); string serializedContext = Console.ReadLine(); ArgUtil.NotNullOrEmpty(serializedContext, nameof(serializedContext)); AgentTaskPluginExecutionContext executionContext = StringUtil.ConvertFromJson <AgentTaskPluginExecutionContext>(serializedContext); ArgUtil.NotNull(executionContext, nameof(executionContext)); VariableValue culture; ArgUtil.NotNull(executionContext.Variables, nameof(executionContext.Variables)); if (executionContext.Variables.TryGetValue("system.culture", out culture) && !string.IsNullOrEmpty(culture?.Value)) { CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(culture.Value); CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(culture.Value); } AssemblyLoadContext.Default.Resolving += ResolveAssembly; try { Type type = Type.GetType(assemblyQualifiedName, throwOnError: true); var taskPlugin = Activator.CreateInstance(type) as IAgentTaskPlugin; ArgUtil.NotNull(taskPlugin, nameof(taskPlugin)); taskPlugin.RunAsync(executionContext, tokenSource.Token).GetAwaiter().GetResult(); } catch (Exception ex) { // any exception throw from plugin will fail the task. executionContext.Error(ex.Message); executionContext.Debug(ex.StackTrace); } finally { AssemblyLoadContext.Default.Resolving -= ResolveAssembly; } return(0); } else if (string.Equals("command", pluginType, StringComparison.OrdinalIgnoreCase)) { string assemblyQualifiedName = args[1]; ArgUtil.NotNullOrEmpty(assemblyQualifiedName, nameof(assemblyQualifiedName)); string serializedContext = Console.ReadLine(); ArgUtil.NotNullOrEmpty(serializedContext, nameof(serializedContext)); AgentCommandPluginExecutionContext executionContext = StringUtil.ConvertFromJson <AgentCommandPluginExecutionContext>(serializedContext); ArgUtil.NotNull(executionContext, nameof(executionContext)); AssemblyLoadContext.Default.Resolving += ResolveAssembly; try { Type type = Type.GetType(assemblyQualifiedName, throwOnError: true); var commandPlugin = Activator.CreateInstance(type) as IAgentCommandPlugin; ArgUtil.NotNull(commandPlugin, nameof(commandPlugin)); commandPlugin.ProcessCommandAsync(executionContext, tokenSource.Token).GetAwaiter().GetResult(); } catch (Exception ex) { // any exception throw from plugin will fail the command. executionContext.Error(ex.ToString()); } finally { AssemblyLoadContext.Default.Resolving -= ResolveAssembly; } return(0); } else if (string.Equals("log", pluginType, StringComparison.OrdinalIgnoreCase)) { // read commandline arg to get the instance id var instanceId = args[1]; ArgUtil.NotNullOrEmpty(instanceId, nameof(instanceId)); // read STDIN, the first line will be the HostContext for the log plugin host string serializedContext = Console.ReadLine(); ArgUtil.NotNullOrEmpty(serializedContext, nameof(serializedContext)); AgentLogPluginHostContext hostContext = StringUtil.ConvertFromJson <AgentLogPluginHostContext>(serializedContext); ArgUtil.NotNull(hostContext, nameof(hostContext)); // create plugin object base on plugin assembly names from the HostContext List <IAgentLogPlugin> logPlugins = new List <IAgentLogPlugin>(); AssemblyLoadContext.Default.Resolving += ResolveAssembly; try { foreach (var pluginAssembly in hostContext.PluginAssemblies) { try { Type type = Type.GetType(pluginAssembly, throwOnError: true); var logPlugin = Activator.CreateInstance(type) as IAgentLogPlugin; ArgUtil.NotNull(logPlugin, nameof(logPlugin)); logPlugins.Add(logPlugin); } catch (Exception ex) { // any exception throw from plugin will get trace and ignore, error from plugins will not fail the job. Console.WriteLine($"Unable to load plugin '{pluginAssembly}': {ex}"); } } } finally { AssemblyLoadContext.Default.Resolving -= ResolveAssembly; } // start the log plugin host var logPluginHost = new AgentLogPluginHost(hostContext, logPlugins); Task hostTask = logPluginHost.Run(); while (true) { var consoleInput = Console.ReadLine(); if (string.Equals(consoleInput, $"##vso[logplugin.finish]{instanceId}", StringComparison.OrdinalIgnoreCase)) { // singal all plugins, the job has finished. // plugin need to start their finalize process. logPluginHost.Finish(); break; } else { // the format is TimelineRecordId(GUID):Output(String) logPluginHost.EnqueueOutput(consoleInput); } } // wait for the host to finish. hostTask.GetAwaiter().GetResult(); return(0); } else { throw new ArgumentOutOfRangeException(pluginType); } } catch (Exception ex) { // infrastructure failure. Console.Error.WriteLine(ex.ToString()); return(1); } finally { Console.CancelKeyPress -= Console_CancelKeyPress; } }