e2etest/GuestProxyAgentTest/TestScenarios/TestScenarioBase.cs (278 lines of code) (raw):

// Copyright (c) Microsoft Corporation // SPDX-License-Identifier: MIT using Azure.ResourceManager.Compute; using GuestProxyAgentTest.Extensions; using GuestProxyAgentTest.Models; using GuestProxyAgentTest.Settings; using GuestProxyAgentTest.TestCases; using GuestProxyAgentTest.Utilities; using System.Diagnostics; namespace GuestProxyAgentTest.TestScenarios { /// <summary> /// Class for trigger each Test Scenario /// </summary> public abstract class TestScenarioBase { private TestScenarioSetting _testScenarioSetting = null!; private VMBuilder _vmBuilder = null!; private JunitTestResultBuilder _junitTestResultBuilder = null!; private List<TestCaseBase> _testCases = new List<TestCaseBase>(); protected bool EnableProxyAgentForNewVM { get; set; } public TestScenarioBase() { TestScenarioSetup(); } public TestScenarioBase TestScenarioSetting(TestScenarioSetting testScenarioSetting) { this._testScenarioSetting = testScenarioSetting; this._vmBuilder = new VMBuilder().LoadTestCaseSetting(testScenarioSetting); return this; } public TestScenarioBase JUnitTestResultBuilder(JunitTestResultBuilder junitTestResultBuilder) { this._junitTestResultBuilder = junitTestResultBuilder; return this; } /// <summary> /// Abstract method for sub class(TestScenario) to set up its scenario, including add test case or others settings /// </summary> public abstract void TestScenarioSetup(); /// <summary> /// Add test case for the scenario /// </summary> /// <param name="testCase"></param> protected void AddTestCase(TestCaseBase testCase) { this._testCases.Add(testCase); } private string LogPrefix { get { return "Test Group: " + _testScenarioSetting.testGroupName + ", Test Scenario: " + _testScenarioSetting.testScenarioName + ": "; } } protected void ConsoleLog(string msg) { Console.WriteLine(LogPrefix + msg); } protected void PreCheck() { if (_testCases.Count == 0) { throw new Exception("Test cases list is empty."); } if (_testScenarioSetting == null) { throw new Exception("Test scenario setting is not set."); } if (_junitTestResultBuilder == null) { throw new Exception("JUnit test result builder is not set"); } if (_vmBuilder == null) { throw new Exception("VM builder is not set"); } } /// <summary> /// The template workflow for start a test scenario: /// 1. build VM /// 2. run the test cases one by one /// 3. collect GALogs zip /// 4. write the test result to Junit format. /// 5. save Logs including each test case run and collect GALogs zip /// /// </summary> /// <param name="testScenarioStatusDetails"></param> /// <returns></returns> public async Task StartAsync(TestScenarioStatusDetails testScenarioStatusDetails) { PreCheck(); try { // Create a cancellation token source that will be used to cancel the running test scenario/cases CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); await DoStartAsync(testScenarioStatusDetails, cancellationTokenSource.Token).TimeoutAfter(_testScenarioSetting.testScenarioTimeoutMilliseconds, cancellationTokenSource); } catch (Exception ex) { ConsoleLog($"Test Scenario {_testScenarioSetting.testScenarioName} Exception: {ex.Message}."); // set running test cases to failed // set not started test cases to aborted ex.UpdateTestCaseResults(_testCases, _junitTestResultBuilder, _testScenarioSetting.testScenarioName); } finally { try { await CollectGALogsOnVMAsync(); } catch (Exception ex) { Console.WriteLine("Collect GA Logs error: " + ex.Message); } try { ConsoleLog("Cleanup generated Azure Resources."); VMHelper.Instance.CleanupTestResources(_testScenarioSetting); } catch (Exception ex) { Console.WriteLine("Cleanup azure resources exception: " + ex.Message); } } } private async Task DoStartAsync(TestScenarioStatusDetails testScenarioStatusDetails, CancellationToken cancellationToken) { try { ConsoleLog("Running test."); testScenarioStatusDetails.Status = ScenarioTestStatus.Running; VirtualMachineResource vmr; Stopwatch sw = Stopwatch.StartNew(); var vmCreateTestName = "CreateVM"; try { vmr = await _vmBuilder.Build(this.EnableProxyAgentForNewVM, cancellationToken); ConsoleLog("VM Create succeed"); sw.Stop(); _junitTestResultBuilder.AddSuccessTestResult(_testScenarioSetting.testScenarioName, vmCreateTestName, "VM Create succeed", "", sw.ElapsedMilliseconds); } catch (Exception ex) { // if the VM Creation operation failed, try check the VM instance view for 5 minutes var startTime = DateTime.UtcNow; while (true) { vmr = await _vmBuilder.GetVirtualMachineResource(); var instanceView = await vmr.InstanceViewAsync(cancellationToken: cancellationToken); if (instanceView?.Value?.Statuses?.Count > 0 && (instanceView.Value.Statuses[0].DisplayStatus == "Provisioning succeeded" || instanceView.Value.Statuses[0].DisplayStatus == "VM running")) { ConsoleLog("VM Create succeed"); sw.Stop(); _junitTestResultBuilder.AddSuccessTestResult(_testScenarioSetting.testScenarioName, vmCreateTestName, "VM Create succeed", "", sw.ElapsedMilliseconds); break; } if (DateTime.UtcNow - startTime > TimeSpan.FromMinutes(5)) { // poll timed out, rethrow the exception sw.Stop(); _junitTestResultBuilder.AddFailureTestResult(testScenarioStatusDetails.ScenarioName, vmCreateTestName, "", ex.Message + ex.StackTrace ?? "", "", sw.ElapsedMilliseconds); throw; } // wait for 10 seconds before polling again await Task.Delay(10000); } } ConsoleLog("Running scenario test: " + _testScenarioSetting.testScenarioName); await ScenarioTestAsync(vmr, testScenarioStatusDetails, cancellationToken); } catch (Exception ex) { testScenarioStatusDetails.ErrorMessage = ex.Message; testScenarioStatusDetails.Result = ScenarioTestResult.Failed; ConsoleLog("Exception occurs: " + ex.Message); } testScenarioStatusDetails.Status = ScenarioTestStatus.Completed; ConsoleLog("Test scenario run finished."); } private async Task ScenarioTestAsync(VirtualMachineResource vmr, TestScenarioStatusDetails testScenarioStatusDetails, CancellationToken cancellationToken) { testScenarioStatusDetails.Result = ScenarioTestResult.Succeed; // always running all the cases inside scenario foreach (var testCase in _testCases) { cancellationToken.ThrowIfCancellationRequested(); if (cancellationToken.IsCancellationRequested) { ConsoleLog($"Test case {testCase.TestCaseName} is cancelled."); break; } TestCaseExecutionContext context = new TestCaseExecutionContext(vmr, _testScenarioSetting, cancellationToken); Stopwatch sw = Stopwatch.StartNew(); try { testCase.Result = TestCaseResult.Running; await testCase.StartAsync(context); sw.Stop(); context.TestResultDetails .DownloadContentIfFromBlob() .WriteJUnitTestResult(_junitTestResultBuilder, _testScenarioSetting.testScenarioName, testCase.TestCaseName, sw.ElapsedMilliseconds); if (!context.TestResultDetails.Succeed) { testScenarioStatusDetails.FailedCases.Add(testCase.TestCaseName); testScenarioStatusDetails.Result = ScenarioTestResult.Failed; } } catch (Exception ex) { var errorMessage = $"test case: {testCase.TestCaseName} failed with exception: message: {ex.Message}, stack trace: {ex.StackTrace}"; testScenarioStatusDetails.FailedCases.Add(testCase.TestCaseName); testScenarioStatusDetails.Result = ScenarioTestResult.Failed; context.TestResultDetails.Succeed = false; context.TestResultDetails.StdErr = errorMessage; context.TestResultDetails.FromBlob = false; sw.Stop(); ConsoleLog($"Scenario case {testCase.TestCaseName} exception: {ex.Message}, stack trace: {ex.StackTrace}"); _junitTestResultBuilder.AddFailureTestResult(_testScenarioSetting.testScenarioName, testCase.TestCaseName, "", errorMessage, "", sw.ElapsedMilliseconds); } finally { testCase.Result = context.TestResultDetails.Succeed ? TestCaseResult.Succeed : TestCaseResult.Failed; ConsoleLog($"Scenario case {testCase.TestCaseName} finished with result: {(context.TestResultDetails.Succeed ? "Succeed" : "Failed")} and duration: " + sw.ElapsedMilliseconds + "ms"); SaveResultFile(context.TestResultDetails.CustomOut, $"TestCases/{testCase.TestCaseName}", "customOut.txt", context.TestResultDetails.FromBlob); SaveResultFile(context.TestResultDetails.StdErr, $"TestCases/{testCase.TestCaseName}", "stdErr.txt", context.TestResultDetails.FromBlob); SaveResultFile(context.TestResultDetails.StdOut, $"TestCases/{testCase.TestCaseName}", "stdOut.txt", context.TestResultDetails.FromBlob); } } } private async Task CollectGALogsOnVMAsync() { ConsoleLog("Collecting GA logs on VM."); var vmr = await _vmBuilder.GetVirtualMachineResource(); var logZipPath = Path.Combine(Path.GetTempPath(), _testScenarioSetting.testGroupName + "_" + _testScenarioSetting.testScenarioName + "_VMAgentLogs.zip"); using (File.CreateText(logZipPath)) { ConsoleLog("Created empty VMAgentLogs.zip file."); } var logZipSas = StorageHelper.Instance.Upload2SharedBlob(Constants.SHARED_E2E_TEST_OUTPUT_CONTAINER_NAME, logZipPath, _testScenarioSetting.TestScenarioStorageFolderPrefix); var collectGALogOutput = await RunCommandRunner.ExecuteRunCommandOnVM(vmr, new RunCommandSettingBuilder() .TestScenarioSetting(_testScenarioSetting) .RunCommandName("CollectInVMGALog") .ScriptFullPath(Path.Combine(TestSetting.Instance.scriptsFolder, Constants.COLLECT_INVM_GA_LOG_SCRIPT_NAME)) , CancellationToken.None , (builder) => { return builder.AddParameter("logZipSas", Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(logZipSas))); }); collectGALogOutput.CustomOut = logZipSas; ConsoleLog("GA log zip collected."); SaveResultFile(collectGALogOutput.StdOut, "collectLogZip", "stdOut.txt"); SaveResultFile(collectGALogOutput.StdErr, "collectLogZip", "stdErr.txt"); SaveResultFile(collectGALogOutput.CustomOut, "collectLogZip", "GALogs.zip"); ConsoleLog("GA log zip saved."); } private void SaveResultFile(string fileContentOrSas, string parentFolderName, string fileName, bool isFromSas = true) { var fileFolder = Path.Combine(TestSetting.Instance.testResultFolder, _testScenarioSetting.testGroupName, _testScenarioSetting.testScenarioName, parentFolderName); Directory.CreateDirectory(fileFolder); var filePath = Path.Combine(fileFolder, fileName); if (isFromSas) { TestCommonUtilities.DownloadFile(fileContentOrSas, filePath, ConsoleLog); } else { File.WriteAllText(filePath, fileContentOrSas); } } } /// <summary> /// Test case execution context class /// container VirtualMachineResource and TestScenarioSetting /// VirtualMachineResource is the created the Azure VM resource for the test scenario /// </summary> public class TestCaseExecutionContext { private VirtualMachineResource _vmr = null!; private TestScenarioSetting _testScenarioSetting = null!; private CancellationToken _cancellationToken; /// <summary> /// TestResultDetails for a particular test case /// </summary> public TestCaseResultDetails TestResultDetails { get; set; } = new TestCaseResultDetails(); public TestScenarioSetting ScenarioSetting { get { return _testScenarioSetting; } } /// <summary> /// the Azure Virtual Machine Resource created for running E2E test /// </summary> public VirtualMachineResource VirtualMachineResource { get { return _vmr; } } public CancellationToken CancellationToken { get { return _cancellationToken; } } public TestCaseExecutionContext(VirtualMachineResource vmr, TestScenarioSetting testScenarioSetting, CancellationToken cancellationToken) { _vmr = vmr; _testScenarioSetting = testScenarioSetting; _cancellationToken = cancellationToken; } } }